Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change style in ModelView (MVVM + WPF)

I have an application developed in WPF using the MVVM pattern (MVVM Light Toolkit).

So far, I had no problems, until it is time to change at runtime the style associated with some of my controls, a set of MenuItems. (They can have up to three different styles).

If I was not working with MVVM I could solve it using the command:

MenuElement_Left4.Style = (Style)FindResource("MenuButtonTabsLeft");

But because I want do it completely in MVVM, I've done these tests to achieve that:

1) Try to change the style with a binding (this has not worked):

<MenuItem x:Name="MenuElement_Left4" Header="Test" Style="{Binding SelectedStyle}">

And in the ViewModel:

public string SelectedStyle
    {
       get { return this.selectedStyle; }
       set { this.selectedStyle = value; 
             RaisePropertyChanged("SelectedStyle");
           }
    }
    private string selectedStyle;

2) Change the style with DataTrigger (this has not worked too. Raises an exception (Style Trigger to Apply another Style)):

<MenuItem.Style>
    <Style TargetType="{x:Type MenuItem}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=TestStyle}" Value="True">
                <Setter Property="Style" Value="{StaticResource  MenuButtonTabsLeftArrow}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=TestStyle}" Value="False">
                <Setter Property="Style" Value="{StaticResource  MenuButtonTabsLeft}"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</MenuItem.Style>

At the end, I managed to solve it, using a Combox using the following code (I only use the ComboBox to change the style of the MenuItems so it is invisible). Got the idea from (How can I change an elements style at runtime?):

<MenuItem x:Name="MenuElement_Left4" Header="Test" Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}">
<ComboBox Name="AvailableStyles" SelectedIndex="{Binding AvailableStylesIndex}" Visibility="Collapsed">
    <ComboBoxItem Tag="{x:Null}">None</ComboBoxItem>
    <ComboBoxItem Tag="{StaticResource MenuButtonTabsLeftArrow}">MenuButtonTabsLeftArrow</ComboBoxItem>
    <ComboBoxItem Tag="{StaticResource MenuButtonTabsLeft}">MenuButtonTabsLeft</ComboBoxItem>
</ComboBox>

And in my ViewModel:

public int AvailableStylesIndex
{
    get { return this.availableStylesIndex; }
    set
    {
        this.availableStylesIndex = value;
        RaisePropertyChanged("AvailableStylesIndex");
    }
}

I'd rather use a cleaner way. Any suggestions? A piece of code would be very helpful.

like image 458
Josep B. Avatar asked Sep 19 '13 08:09

Josep B.


1 Answers

Since you are keeping your styles in you resources, cleaner approch would be to use IMultiValueConverter's with you first approch something like this:

ViewModel

public string SelectedStyle
{
   get { return this.selectedStyle; }
   set { this.selectedStyle = value; 
         RaisePropertyChanged("SelectedStyle");
       }
}
private string selectedStyle;

Xaml:

<MenuItem.Style>
    <MultiBinding Converter="{StaticResource StyleConverter}">
        <MultiBinding.Bindings>
            <Binding RelativeSource="{RelativeSource Self}"/>
            <Binding Path="SelectedStyle"/>
        </MultiBinding.Bindings>
    </MultiBinding>
</MenuItem.Style/>

In the converter find the style you want and apply it

class StyleConverter : IMultiValueConverter 
{
     public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        FrameworkElement targetElement = values[0] as FrameworkElement; 
        string styleName = values[1] as string;

        if (styleName == null)
            return null;

        Style newStyle = (Style)targetElement.TryFindResource(styleName);

        if (newStyle == null)
            newStyle = (Style)targetElement.TryFindResource("MyDefaultStyleName");

        return newStyle;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Taken out from Steven Robbins's answer in this post

like image 189
Omri Btian Avatar answered Nov 09 '22 22:11

Omri Btian