当我们需要在某些条件下隐藏DataGrid的列时,我们都会遇到问题。至少有两种方法可以解决这个问题。方法的流程需要代理元素。我使用这些方法。如您所见,使用FreezableProxy
作为代理元素不需要ContentControl
,但需要指定其他类(FreezableProxy
)。使用FrameworkElement
作为代理不需要指定其他类(如FreezableProxy
),但需要在标记中添加ContentControl
。
XAML:
<Window.Resources>
<SampleRadioBoxCheckedConverter:FreezableProxy x:Key="FreezableProxy" Data="{Binding}"/>
<FrameworkElement x:Key="FrameworkElement" DataContext="{Binding}"/>
</Window.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"/>
<DataGridTextColumn Header="Type"
Visibility="{Binding Data.IsPartnerColumnVisible, Source={StaticResource FreezableProxy}}"/>
<DataGridTextColumn Header="Number"
Visibility="{Binding DataContext.IsPartnerColumnVisible, Source={StaticResource FrameworkElement}}"/>
</DataGrid.Columns>
</DataGrid>
<ContentControl Grid.Row="1" Content="{StaticResource FrameworkElement}" Visibility="Collapsed"></ContentControl>
</Grid>
代码隐藏:
public class FreezableProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new FreezableProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(FreezableProxy));
}
public partial class MainWindow : INotifyPropertyChanged
{
private Visibility _isPartnerColumnVisible = Visibility.Hidden;
public Visibility IsPartnerColumnVisible
{
get
{
return _isPartnerColumnVisible;
}
set
{
_isPartnerColumnVisible = value;
RaisePropertyChanged("IsPartnerColumnVisible");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(String prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
Bouth方法看起来很相似。建议我们使用第二种方法(使用FrameworkElement
)。然后我有一个问题 - 如果ContentControl
实际设置为Binding
,为什么我需要指定其他{StaticResource FrameworkElement}
? ContentControl
做了什么魔术?当我们使用FrezableProxy
时,我们不需要指定任何ContentControl-s
或其他内容,它可以正常工作。
更新
<FrameworkElement x:Key="FrameworkElement" DataContext="{Binding}"/>
/////<ContentControl Grid.Row="1" Content="{StaticResource FrameworkElement}" Visibility="Collapsed"></ContentControl>
为什么这不起作用? ContentControl已注释,但我明确设置了DataContext
的{{1}}属性。
答案 0 :(得分:2)
问题是 DataGridColumns与其父dataGrid 不在同一个Visual树中。因此,绑定不起作用,因为使用RelativeSource无法找到DataContext,因为它依赖于Visual Tree。
因此,提到的所有方法都是将DataContext传递给列。
现在,区分两种方法:
FreezableProxy方法:
实际的参考资料是here。 Freezable背后的魔法在这里定义,所以引用相同的链接:
Freezable类的主要目的是定义具有的对象 可修改和只读状态,但我们的有趣功能 case是 Freezable对象即使在什么时候也可以继承DataContext 它们不在视觉或逻辑树中 。
因此,您不需要任何ContentControl或任何代理控制,因为使用freezable我们会自动获得继承的DataContext。
FrameworkElement + ContentControl方法:
使用标志 FrameworkMetadataOptions.Inherits
声明DataContext属性。这意味着子控件会自动从它的父元素继承它,除非为子控件明确设置。
所以ContentControl自动从Grid继承DataContext,ContentControl的子元素将从ContentControl继承它。这就是FrameworkElement从ContentControl继承它的原因。 (无需手动绑定DataContext)。这将有效:
<FrameworkElement x:Key="FrameworkElement"/>
另一种方法是在我的回答中使用 x:Reference
,如here所述。
答案 1 :(得分:0)
你错了。使用过的元素不必须是ContentControl
,而且可以是任何FrameworkElement
。您似乎也对此方法的实际工作方式感到困惑,因为您 使用x:Reference
中所需的Binding
指令:
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"/>
<DataGridTextColumn Header="Number"
Visibility="{Binding DataContext.IsPartnerColumnVisible,
Source={x:Reference FrameworkElement}}"/>
</DataGrid.Columns>
</DataGrid>
<FrameworkElement Name="someElement" Grid.Row="1" Visibility="Collapsed" />
</Grid>
导致此问题是因为DataGridTextColumn
不是主视觉树的一部分。我们可以在这里使用x:Reference
指令,因为它不依赖于源元素与它所使用的Binding
在同一个可视树中。简而言之,我们只是使用这个{{1这里(可以是任何控件),这样我们就可以通过它从主视觉树中访问FrameworkElement
。