Как предотвратить переход столбца DataGridTemplateColumn в режим редактирования на основе некоторого условия?


У меня есть столбец DataGridTemplateColumn, который можно редактировать. Я хочу, чтобы пользователь мог редактировать содержимое ячейки в этом столбце только в том случае, если бизнес-объект удовлетворяет некоторому критерию. Предположим, что мой бизнес-объект реализует INotifyPropertyChanged и имеет три свойства: Name, отдел , и продажи. название иотдел являются строками, апродажи - двойником.

Я хочу, чтобы пользователь мог редактировать только значение Sales если отдел равен "рознице". Вот datagrid, который я мог бы использовать для этого:

<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>

Я использую панель стека с двумя текстовыми элементами. Я использую привязку в видимости текстовых элементов для переключения текстовых элементов. Если у меня есть строка со значениемDepartment , которое не является "Retail", я отображаю текстовый блок в столбцеSales независимо от того, находится ли ячейка в режиме отображения или в режиме редактирования.

Это похоже на топорное решение для меня. Есть ли какой-то способ предотвратить это? типы ячеек от входа в режим редактирования полностью? Я хочу разрешить режим редактирования только в том случае, если Отдел является "розничным". Возможно ли это?

Правка: добавление кода.

@Rachel. Спасибо за вашу помощь. Я хочу вставить весь мой код datagrid 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, появляется текстовое поле, но в нем нет значения (см. здесь ). Я не уверен, почему он не имеет значения, потому что текстовое поле в TextBoxTemplate указывает привязку. Вы знаете, почему это происходит?

Edit: я заметил еще одну проблему с этим решением, я не могу редактировать значение в столбце Sales. Если я попытаюсь, значение возвращается к исходному значению предварительного редактирования.

3 2

3 ответа:

Я бы использовал DataTrigger, который переключает значение свойства, например TextBox.IsReadOnly, основываясь на том, равен ли отдел "розничной торговле" или нет

<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, чтобы полностью отключить редактирование, и установить этот стиль на TextBox в вашем DataGridTemplateColumn. Это избавило бы вас от многих лишних 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/TextBlock в вашем DataTemplate вместо StackPanel и нескольких объектов.

И если вы действительно хотите, чтобы он отображал фактический TextBlock вместо TextBox, когда пользователь не имеет возможности редактировать, вы можете использовать ContentControl и переключать его свойство ContentTemplate с DataTrigger

<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>

Вы также можете подписаться на событие BeginningEdit DataGrid, а затем добавить простую проверку кода.

В 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; }
}

Я заставил его работать, используя этот код. Я не совсем понимаю его, но он работает так, как я хочу, чтобы он работал. Спасибо, Рейчел!

<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>