2

我有一个可编辑的 DataGridTemplateColumn。如果业务对象满足某些标准,我只希望用户能够编辑此列中单元格的内容。假设我的业务对象实现了 INotifyPropertyChanged 并具有三个属性:NameDepartmentSalesNameDepartment是字符串,Sales是双精度数。

我希望用户只有在部门等于“零售”时才能编辑销售值。这是我可能用来执行此操作的数据网格:

<DataGrid ItemsSource="{Binding Path=MyTypeCollection}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Path=Name, Mode=OneTime}" IsReadOnly="True" />
        <DataGridTextColumn Header="Department" Binding="{Binding Path=Department, Mode=OneTime}" IsReadOnly="True" />
        <DataGridTemplateColumn Header="Sales">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Path=Sales, Mode=TwoWay}" Visibility="{Binding Path={StaticResource ResourceKey=IsRetail}}" />
                        <TextBlock Text="{Binding Path=Sales, Mode=OneWay}" Visibility="{Binding Path={StaticResource ResourceKey=IsNotRetail}}" />
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBox Text="{Binding Path=Sales, Mode=TwoWay}" Visibility="{Binding Path={StaticResource ResourceKey=IsRetail}}" />
                        <TextBlock Text="{Binding Path=Sales, Mode=OneWay}" Visibility="{Binding Path={StaticResource ResourceKey=IsNotRetail}}" />
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

我正在使用带有两个 Text 元素的堆栈面板。我在 Text 元素的 Visibility 中使用绑定来切换 Text 元素。如果我有一行的部门值不是“零售”,则无论单元格处于显示模式还是编辑模式,我都会在销售列中显示一个 TextBlock。

这对我来说似乎是一个笨拙的解决方案。有什么方法可以阻止这些类型的单元格完全进入编辑模式?我只想在部门为“零售”的情况下允许编辑模式。这可能吗?

编辑:添加代码。

@雷切尔。感谢您的帮助。我想粘贴我所有的数据网格 XAML 代码,以确保一切正常。

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=Data}">

    <DataGrid.Resources>
        <DataTemplate x:Key="TextBoxTemplate">
            <TextBox Text="{Binding Path=Sales}" />
        </DataTemplate>
    </DataGrid.Resources>

    <DataGrid.Columns>
        <DataGridTextColumn Header="Department" Binding="{Binding Path=Department, Mode=OneTime}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Path=Name, Mode=OneTime}" />

        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ContentControl x:Name="salesControl">
                        <TextBlock Text="{Binding Sales}" />
                    </ContentControl>

                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding Department}" Value="Retail">
                            <Setter TargetName="salesControl" Property="ContentTemplate" Value="{StaticResource TextBoxTemplate}" />
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>

            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Sales}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>
</DataGrid>

我几乎得到了我想要的地方。当我单击一行Sales中的一个值时Retail,会出现一个 TextBox,但其中没有值(请参见此处)。我不确定为什么它没有值,因为 TextBoxTemplate 中的 TextBox 指定了一个绑定。你知道这是为什么吗?

编辑:我注意到此解决方案的另一个问题,我实际上无法编辑销售列中的值。如果我尝试,该值将恢复为原始的预编辑值。

4

3 回答 3

3

我会使用 aDataTrigger来切换属性的值,例如TextBox.IsReadOnly根据 Department 是否等于“Retail”

<Style ...>
    <!-- Set Default -->
    <Setter Property="IsReadOnly" Value="True" />

    <Style.Triggers>
        <DataTrigger Binding="{Binding Department}" Value="Retail">
            <Setter Property="IsReadOnly" Value="False" />
        </DataTrigger>
    </Style.Triggers>
</Style>

如果您不需要在 DataGrid 中进行任何其他编辑,最简单的方法是IsReadOnly="True"在您DataGrid的上设置完全禁用编辑,然后TextBoxDataGridTemplateColumn. 这将摆脱很多额外的 XAML 代码,比如IsReadOnly="True"

<DataGrid ItemsSource="{Binding Path=MyTypeCollection}" 
          AutoGenerateColumns="False"
          IsReadOnly="True">

    <!-- This could also go in Window.Resources, UserControl.Resources, etc -->
    <DataGrid.Resources>
        <Style x:Key="SalesTextBoxStyle" TargetType="{x:Type TextBox}">
            <!-- Set Default -->
            <Setter Property="IsReadOnly" Value="True" />

            <Style.Triggers>
                <DataTrigger Binding="{Binding Department}" Value="Retail">
                    <Setter Property="IsReadOnly" Value="False" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.Resources>

    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}"/>
        <DataGridTextColumn Header="Department" Binding="{Binding Path=Department}" />
        <DataGridTemplateColumn Header="Sales">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=Sales}" 
                             Style="{StaticResource SalesTextBoxStyle}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

如果您确实需要默认的 DataGrid 编辑功能,您仍然可以使用相同的东西,但您只需要一个TextBox/TextBlockDataTemplate不是StackPanel多个对象。

如果您真的希望它在用户无法编辑时显示实际TextBlock而不是 a ,您可以使用 a并使用 a 切换它的属性TextBoxContentControlContentTemplateDataTrigger

<DataGrid.Resources>
    <DataTemplate x:Key="TextBoxTemplate">
        <TextBox Text="{Binding Path=.}" />
    </DataTemplate>
</DataGrid.Resources>

...

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ContentControl x:Name="salesControl" Content="{Binding Sales}" />
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Department}" Value="Retail">
                    <Setter TargetName="salesControl" 
                            Property="ContentTemplate" 
                            Value="{StaticResource TextBoxTemplate}" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
于 2013-03-21T16:48:52.223 回答
1

您还可以订阅BeginningEditDataGrid 的事件,然后在代码隐藏中添加一个简单的检查。

在 XAML 中:

<DataGrid BeginningEdit="DataGrid_BeginningEdit" />

示例代码:

private void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
    RowViewModel VM = (RowViewModel)((DataGrid)sender).SelectedItem;

    if (!VM.IsRetail) { e.Cancel = true; }
}
于 2013-03-21T20:01:35.647 回答
1

我让它使用这个代码工作。我不完全理解它,但它可以按我的意愿工作。谢谢雷切尔!

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=Data}">

    <DataGrid.Resources>
        <DataTemplate x:Key="TextBoxTemplate">
            <TextBox Text="{Binding Path=Text, StringFormat=c0}" />
        </DataTemplate>
    </DataGrid.Resources>

    <DataGrid.Columns>
        <DataGridTextColumn Header="Department" Binding="{Binding Path=Department, Mode=OneTime}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Path=Name, Mode=OneTime}" />

        <DataGridTemplateColumn Header="Sales">
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ContentControl x:Name="salesControl" DataContext="{Binding Path=.}">
                        <TextBlock Text="{Binding Path=Sales, Mode=TwoWay, StringFormat=c0}" />
                    </ContentControl>

                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding Department}" Value="Retail">
                            <Setter TargetName="salesControl" Property="ContentTemplate" Value="{StaticResource TextBoxTemplate}" />
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>

            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Sales, StringFormat=c0}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>
</DataGrid>
于 2013-03-21T20:36:50.330 回答