使用IValueConverter实现动态样式绑定

5
我正在尝试在加载用户控件时动态设置一个在 App.xaml 中定义的样式,但某些原因导致样式未能应用(即没有发生错误,只是未应用样式)。
我确定这是因为我定义了错误的绑定方式,但我无法找出需要做出哪些不同的更改才能使其正常工作。

App.xaml 样式

我想要的样式是 RunningTitleBlock,它由我在下面的代码示例中包含的几个其他样式组成。
<Style TargetType="Label">
    <Setter Property="Margin" Value="4"/>
</Style>

<Style TargetType="Label"
       BasedOn="{StaticResource {x:Type Label}}"
       x:Key="HeaderBlock">
    <Setter Property="FontSize" Value="16"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="Foreground" Value="White"/>
</Style>

<Style TargetType="Label"
       BasedOn="{StaticResource ResourceKey=HeaderBlock}"
       x:Key="TitleBlock">
    <Setter Property="Foreground" Value="Black"/>
</Style>

<Style TargetType="Label"
       BasedOn="{StaticResource ResourceKey=TitleBlock}"
       x:Key="RunningTitleBlock">
    <Setter Property="Background">
        <Setter.Value>
            <LinearGradientBrush StartPoint="0.0, 0.5"
                                 EndPoint="1.0, 0.5">
                <GradientStop Color="White" Offset="0.0"/>
                <GradientStop Color="Green" Offset="1.0"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
</Style>

用户控件的绑定

我试图让绑定绑定到从值转换器返回的值。

Style="{DynamicResource ResourceKey={Binding Path=MonitoringType, Converter={StaticResource TSConverter}}}"

代码

MonitoringTypes 枚举

public enum MonitoringTypes
{
    Running,
    Failed,
    Paused,
    Favorites,
}

用户控件

这里我想做的是将传入的MonitoringTypes枚举值的字符串值与一些已知文本连接起来,以构建在App.xaml中存在的样式名称。值转换器被调用并且返回了正确的值,但由于某种原因样式没有应用。

/// <summary>
/// Interaction logic for MonitorWorkflow.xaml
/// </summary>
public partial class MonitorWorkflow : UserControl
{
    public MonitorWorkflow(MonitoringTypes monitoringType)
    {
        InitializeComponent();

        this.DataContext = new MonitorWorkflowViewModel { MonitoringType = monitoringType };
    }
}

public class MonitorWorkflowViewModel
{
    public MonitoringTypes MonitoringType { get; set; }
}

public class TitleStyleValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var type = (MonitoringTypes)value;
        return string.Format("{0}TitleBlock", Enum.GetName(typeof(MonitoringTypes), type));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return Enum.Parse(typeof(MonitoringTypes), value.ToString().Substring(0, value.ToString().IndexOf("TitleBlock")));
    }
}

3
我会尽可能地为您翻译:我可能会使用IValueConverter并返回样式。 - patrick
3
您需要绑定的属性需要是依赖属性,或者您需要实现 INotifyPropertyChanged 接口来在值更新时通知绑定。在您的情况下,当枚举值更改时,您需要通知您的“TitleBlockStyle”属性已更改(将其包装在属性中)。最好使用 ValueConverter 并像此示例中绑定到您的枚举:https://dev59.com/om445IYBdhLWcg3wBVrz - Trevor Elliott
@patrickinmpls,我已经更新了我的问题,展示了我尝试使用值转换器的地方。 - Mike Perrenoud
@Moozhe,我已经更新了我的问题,展示了我尝试使用值转换器的地方。 - Mike Perrenoud
@code4life,那你是想看看如果我不尝试继承,是否只应用了“Background”? - Mike Perrenoud
抱歉,我匆忙阅读了帖子,跳过了一些重要的点。对此感到抱歉。我会重新发布。 - code4life
2个回答

6

我的建议是跳过DynamicResource语句,直接使用Converter来提供Style

Style="{Binding Path=MonitoringType, Converter={StaticResource TSConverter}}"

TSConverter 中,你可以返回一个Style而不是一个字符串。像这样:
public class TitleStyleValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, 
        System.Globalization.CultureInfo culture)
    {
        var type = (MonitoringTypes)value;
        var styleToReturn = FindResource(
            string.Format("{0}TitleBlock", 
                Enum.GetName(typeof(MonitoringTypes), type)));
        if (styleToReturn != null)
            return (Style)styleToReturn;
        else 
            return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        // not sure if you need this anymore... 
        return Enum.Parse(typeof(MonitoringTypes), value.ToString().Substring(0,
           value.ToString().IndexOf("TitleBlock")));
    }
}

这是我用下面的代码实现的。实际上,我自己解决了问题,而你也同时回答了我的问题。时机很好!
public class TitleStyleValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var type = (MonitoringTypes)value;
        return App.Current.Resources[string.Format("{0}TitleBlock", Enum.GetName(typeof(MonitoringTypes), type))];
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return Enum.Parse(typeof(MonitoringTypes), value.ToString().Substring(0, value.ToString().IndexOf("TitleBlock")));
    }
}

@Mike,我对你的代码思考了一下,想知道你是否考虑过使用“DataTriggers”来强制实施样式,而不是采用这种方法。对于你正在进行的样式处理方式,这就是我一直在使用的方法。 - code4life
我认为你是正确的 - 这样更为简洁。我可以构建一个样式,对标签的样式进行简单的“静态资源”绑定,然后使用“数据触发器”来切换样式。 - Mike Perrenoud

1
public static Style DayModeButton = null; 

void loadStyle() 
{
Uri uri1 = new Uri("/Resources/ButtonStyle.xaml", UriKind.Relative);
                ResourceDictionary resDict1 = Application.LoadComponent(uri1) as ResourceDictionary;

foreach (object obj in resDict1.Values) //Set explicit reference
     if (obj is Style) DayModeButton = (Style)obj;
}



[ValueConversion(typeof(object), typeof(Style))]
public class GetStyleConverter: IValueConverter
{
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
               return DayModeButton ; 
         }
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return 0;
         }
}

也许可以加上一些注释来解释你的代码 :) - pix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接