![](/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.