![](/img/trans.png)
[英]Set DataGridCell style depending on CheckBox Checked state in DataGridTemplateColumn
[英]WPF style depending on checkbox state
我正在创建一个设置编辑器,插件编写者可以在其中定义自己的用户界面来配置其插件。 我正在实现一项功能,如果未选中复选框,则可以隐藏某些“高级”元素。
XAML复选框很简单:
<CheckBox Name="isAdvanced">_Advanced</CheckBox>
理想情况下(稍后会详细介绍),实现者只需向高级控件添加一个标志(当未选中“高级”复选框时应将其隐藏),如下所示:
<Button library:MyLibraryControl.IsAdvanced="True">My Button</Button>
问题在于,在isAdvanced.IsChecked == false
时隐藏IsAdvanced="True"
元素具有isAdvanced.IsChecked == false
。 我在window元素上具有此样式的预期行为:
<Window.Resources>
<Style TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding (library:MyLibraryControl.IsAdvanced), RelativeSource={RelativeSource Mode=Self}}" Value="True" />
<Condition Binding="{Binding IsChecked, ElementName=isAdvanced}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="UIElement.Visibility" Value="Collapsed" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
但是,此方法存在两个问题:
IsAdvanced
标志添加到任何视觉元素。 还有其他产生我想要的功能的方法吗? 我不担心在背后的代码中工作,但是一个优雅的XAML解决方案是理想的(除了在用户首选项中保存复选框的状态之外,这纯粹是一个UI更改)。
想到了一些其他表示高级元素的方法。 其中包括使用动态资源并直接绑定:
<Button Visibility="{DynamicResource IsAdvancedVisibility}">My Button</Button>
<Button Visibility="{Binding IsChecked, RelativeSource={...}, ValueConverter={...}}">My Button</Button>
使用资源字典可能会起作用,但是似乎很糟糕,因为UI状态似乎不应该属于字典。 手动绑定相当麻烦,因为必须将复选框的状态以某种方式发送到元素,除了硬编码值之外,我看不出它不会变成混乱。
这两种替代解决方案都将语义(“这是一个高级选项”)与外观(“高级选项应折叠”)联系在一起。 来自HTML世界,我知道这是一件非常不好的事情,除非绝对必要,否则我拒绝提交这些方法。
将其移到ViewModel中而不是XAML中如何操作,因为在我看来这就像行为。
在我看来,您想要的行为-每个插件都将一堆属性(映射到UI控件)注册为高级。 有一个全局设置可以打开/关闭高级属性。 发生这种情况时,请更新所有插件以显示/隐藏其高级属性
让插件编写者实现仅包含一组属性AreAdvancedControlsVisible的接口。 让他们负责通过属性更改处理程序在其UI中隐藏/显示控件。 高级UI控件可以绑定到pluginVM上的ShowAdvancedControls标志,该标志可从prop更改的处理程序打开/关闭。 只要设置了ShowAdvanced复选框,该框架就可以循环可用的插件并设置此标志。
解决这个问题可能有很多更好的方法,但是我试图解决您在解决方案中遇到的两个问题。 与此相关的小样本项目可以在这里下载。
1.它只为按钮添加了功能,没有别的。 可以(应该)将IsAdvanced标志添加到任何视觉元素。
将附加属性(使所有子代继承该值)添加到最顶部的容器可以解决此问题。
2.它替换/替代原本在按钮上的样式。
Bea Stollnitz 在这里有一篇不错的关于合并样式的博客文章。
它有一种称为Merge的样式扩展方法,可以使用。
听起来很简单,但是以下问题使代码更加复杂。
1.继承附加属性时,Visual元素没有样式。 必需的已加载事件。
2.样式在使用时无法修改。 样式需要一种复制方法。
因此,我们希望此样式与父容器中所有子项的活动样式合并。
<Style x:Key="IsAdvancedStyle">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding (library:MyLibraryControl.IsAdvanced), RelativeSource={RelativeSource Mode=Self}}" Value="True" />
<Condition Binding="{Binding IsChecked, ElementName=isAdvanced}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Control.Visibility" Value="Collapsed" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
如果根容器是StackPanel,则添加它。 然后,样式IsAdvancedStyle将被所有子级继承,并与活动样式合并。
<StackPanel local:StyleChildsBehavior.StyleChilds="{StaticResource IsAdvancedStyle}">
StyleChildsBehavior.cs
public class StyleChildsBehavior
{
public static readonly DependencyProperty StyleChildsProperty =
DependencyProperty.RegisterAttached("StyleChilds",
typeof(Style),
typeof(StyleChildsBehavior),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits,
StyleChildsCallback));
public static void SetStyleChilds(DependencyObject element, Style value)
{
element.SetValue(StyleChildsProperty, value);
}
public static Style GetStyleChilds(DependencyObject element)
{
return (Style)element.GetValue(StyleChildsProperty);
}
private static void StyleChildsCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(d) == true)
{
return;
}
Style isAdvancedStyle = e.NewValue as Style;
if (isAdvancedStyle != null)
{
FrameworkElement element = d as FrameworkElement;
if (element != null)
{
if (element.IsLoaded == false)
{
RoutedEventHandler loadedEventHandler = null;
loadedEventHandler = new RoutedEventHandler(delegate
{
element.Loaded -= loadedEventHandler;
MergeStyles(element, isAdvancedStyle);
});
element.Loaded += loadedEventHandler;
}
else
{
MergeStyles(element, isAdvancedStyle);
}
}
}
}
private static void MergeStyles(FrameworkElement element, Style isAdvancedStyle)
{
if (element != null)
{
Style advancedStyle = GetStyleCopy(isAdvancedStyle);
advancedStyle.Merge(element.Style);
element.Style = advancedStyle;
}
}
private static Style GetStyleCopy(Style style)
{
string savedStyle = XamlWriter.Save(style);
using (MemoryStream memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(savedStyle)))
{
ParserContext parserContext = new ParserContext();
parserContext.XmlnsDictionary.Add("library", "clr-namespace:HideAll;assembly=HideAll");
return XamlReader.Load(memoryStream, parserContext) as Style;
}
}
}
此后,IsAdvancedStyle将合并到StackPanel的所有子级中,这也适用于在运行时添加的子级。
博客链接中的修改的合并扩展方法。
public static void Merge(this Style style1, Style style2)
{
if (style1 == null || style2 == null)
{
return;
}
if (style1.TargetType.IsAssignableFrom(style2.TargetType))
{
style1.TargetType = style2.TargetType;
}
if (style2.BasedOn != null)
{
Merge(style1, style2.BasedOn);
}
foreach (SetterBase currentSetter in style2.Setters)
{
style1.Setters.Add(currentSetter);
}
foreach (TriggerBase currentTrigger in style2.Triggers)
{
style1.Triggers.Add(currentTrigger);
}
}
我决定将问题稍微解决一下,并且效果很好。
我不使用样式,而是使用Gishu建议的属性绑定。 但是,我没有将UI放置在VM中(属性将在其中手动传播多个层),而是使用了一个名为ShowAdvanced
的附加属性,该属性通过属性继承向下传播。
创建此属性很简单:
public static readonly DependencyProperty ShowAdvancedProperty;
ShowAdvancedProperty = DependencyProperty.RegisterAttached(
"ShowAdvanced",
typeof(bool),
typeof(MyLibraryControl),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior
)
);
复选框在整个窗口上方设置了ShowAdvanced
属性。 它可以将其设置在其他位置(例如在网格上),但是将其放在窗口上更符合IMO的要求:
<CheckBox Grid.Column="0"
IsChecked="{Binding (library:MyLibraryControl.ShowAdvanced), ElementName=settingsWindow}"
Content="_Advanced" />
根据ShowAdvanced
属性更改可见性(或所需的其他任何属性)变得容易:
<Foo.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Foo.Resources>
<Button Visibility="{Binding (library:MyLibraryControl.ShowAdvanced), RelativeSource={RelativeSource Self}, Converter={StaticResource BooleanToVisibilityConverter}}">I'm Advanced</Button>
沟渠样式使插件编写者可以根据需要完全更改控件的布局。 它们还可以显示高级控件,但根据需要将其禁用。 样式带来了很多问题,而且正如Meleak所展示的那样, 解决方法很杂乱 。
我在VM中放置“高级”显示逻辑的主要问题是,现在不再可能将多个视图绑定到同一个VM,同时又保持所需的灵活性。 如果VM中存在“高级”逻辑,则必须显示所有视图的高级控件或不显示视图。 您不能为一个显示它们,而为另一个隐藏它们。 IMO违反了首先拥有VM的原则。
(感谢所有在此发布的人;它非常有帮助!)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.