【问题标题】:Binding IsReadOnly of a DataGridTextColumn to a DataGridTemplateColumn checkbox IsChecked将 DataGridTextColumn 的 IsReadOnly 绑定到 DataGridTemplateColumn 复选框 IsChecked
【发布时间】:2016-04-15 14:55:47
【问题描述】:

基本上,我有一个带有几列的DataGrid,我想启用(更改IsReadOnly 属性)基于CheckBox IsCheckedDataGridTextColumn,位于另一个DataGridTemplateColumn相同的DataGrid

这里是(重要的部分)代码:

<DataGrid Name="lstTags" Grid.Row="0" ItemsSource="{Binding Path = LinesCollection}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" SelectionMode="Single" LostFocus="lstTags_LostFocus" SelectionChanged="lstTags_SelectionChanged">
    <DataGrid.Columns>
        <DataGridTemplateColumn x:Name="colAutoScale" Header="Auto Scale">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox x:Name="ckbAutoScale" HorizontalAlignment="Center" IsChecked="{Binding AutoScale, UpdateSourceTrigger=PropertyChanged}"/>
                 </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTextColumn Header="Scale" Binding="{Binding Path=Scale}" IsReadOnly="{Binding ElementName ckbAutoScale, Path=IsChecked}" Width="60" />
    </DataGrid.Columns>
</DataGrid>

值得一提的是,我还想把IsChecked属性的值取反,也就是

  • IsChecked = true => IsReadOnly = false;
  • IsChecked = false => IsReadOnly = true.

我可能会通过一个简单的Converter 来实现这一点,但我需要第一部分工作。

编辑:

回答一个好问题,我的目标是禁用相邻单元格(同一行),而不是整个列。

【问题讨论】:

  • 您要禁用整列或相邻单元格??
  • 好点。答案将是相邻的单元格。仔细想想,问题可能比我想象的要复杂。
  • 最简单的解决方案是将 AutoScale 属性绑定到 Scale 列的单元格样式的 IsEnable 属性。请看我的回答,我已经给出了依赖于 UI 和依赖于数据的答案。
  • 如果您想防止逐个单元格地进行编辑,您可能必须改为使用 DataGridTemplateColumn 并使用数据触发器来控制 CellEditTemplate 中显示的内容。

标签: c# wpf data-binding datagrid


【解决方案1】:

为您的 Scale Column 使用以下绑定:

 <DataGridTextColumn Header="Scale" Binding="{Binding Path=Scale}" Width="60" >
      <DataGridTextColumn.CellStyle>
           <Style TargetType="DataGridCell">
                <Setter Property="IsEnabled" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridCellsPanel}},Path=Children[0].Content.Content.AutoScale}" />
           </Style>
      </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

或者只是

<DataGridTextColumn Header="Scale" Binding="{Binding Path=Scale}" Width="60" >
            <DataGridTextColumn.CellStyle>
                <Style TargetType="DataGridCell">
                    <Setter Property="IsEnabled" Value="{Binding Path=AutoScale}" />
                </Style>
            </DataGridTextColumn.CellStyle>
        </DataGridTextColumn>

输出:

PS:上面的解决方案 1 是特定于您的代码,导致 Auto Scale column 位于 0 Index 这就是我使用 Children[0] 的原因 Binding。如果有任何上下文需要,请更改。

【讨论】:

  • 解决方案 2 满足了我的需求,简单干净。谢谢!
  • @lucas.mdo 是的,我更喜欢同样的 :)
【解决方案2】:

这类问题确实是Model-View-ViewModel (MVVM) pattern存在的原因。

使用 MVVM,您可以绑定到具有支持视图所需的确切属性的视图模型。这让模型更关心需要持久化哪些数据。

因此,对于您的问题,您需要创建一个LineViewModel,如下所示:

public class LineViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool _isAutoScale;
    private double _scale;

    public bool IsAutoScale
    {
        get { return _isAutoScale; }
        set
        {
            if (value == _isAutoScale) return;
            _isAutoScale = value;
            OnPropertyChange("IsAutoScale");
            OnPropertyChange("IsReadOnly");
        }
    }

    public double Scale
    {
        get { return _scale; }
        set
        {
            if (value == _scale) return;
            _scale = value;
            OnPropertyChange("Scale");
        }
    }

    public bool IsReadOnly => !IsAutoScale;

    private void OnPropertyChange(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

同时,您还想创建一个名为MainWindowViewModel 的父视图模型(或对您的情况有意义的东西)。这是一个非常粗略的版本:

public class MainWindowViewModel : INotifyPropertyChanged
{
    private List<LineViewModel> _lineViewModels;
    public event PropertyChangedEventHandler PropertyChanged;

    public List<LineViewModel> LineViewModels
    {
        get { return _lineViewModels; }
        set
        {
            if (value == _lineViewModels) return;
            _lineViewModels = value;
            OnPropertyChange("LineViewModels");
        }
    }

    public MainWindowViewModel()
    {
        LineViewModels = new[]
        {
            new { AutoScale = false, Scale = 0.2 },
            new { AutoScale = true, Scale = 0.3 },
            new { AutoScale = false, Scale = 0.4 },
        }
            .Select(
                x => new LineViewModel
                {
                    IsAutoScale = x.AutoScale,
                    Scale = x.Scale
                })
            .ToList();
    }

    private void OnPropertyChange(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

最后,您需要更新您的 XAML 文件,使其看起来像这样:

<Window x:Class="Sandbox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:sandbox="clr-namespace:Sandbox"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Window.DataContext>
        <sandbox:MainWindowViewModel />
    </Window.DataContext>
    <DataGrid ItemsSource="{Binding LineViewModels}"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Stretch"
              AutoGenerateColumns="False"
              CanUserAddRows="False"
              CanUserDeleteRows="False"
              SelectionMode="Single">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Auto Scale">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <CheckBox HorizontalAlignment="Center"
                                  IsChecked="{Binding IsAutoScale}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn Header="Auto Scale">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBox Text="{Binding Scale}"
                                 IsReadOnly="{Binding IsReadOnly}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>

因此,基本上,MainWindow 的视图逻辑由MainWindowViewModel 确定,DataGrid 每一行的视图逻辑由LineViewModel 控制。

请注意,许多用于实现 INotifyPropertyChanged 的样板可以使用库/NuGet 包(如 MVVM Light Toolkit 和 PropertyChanged.Fody)来简化。

【讨论】:

  • 我确实使用了 MVVM 模式,并且我确实有你建议的两个 ViewModel。尽管如此,我还是无法按照我想要的方式工作,可能是因为我试图避免使用 DataGridTemplateColumn 进行缩放。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
  • 2020-11-16
相关资源
最近更新 更多