0

I have a UserControl which has a property Theme. The Theme is just an Enum of ElementTheme.

public sealed partial class PlaylistControl : UserControl, MediaControlListener
{
    public ElementTheme Theme { get; set; }
    public static readonly DependencyProperty ThemeProperty = DependencyProperty.Register("Theme", typeof(ElementTheme), typeof(PlaylistControl), new PropertyMetadata(null));

    ...
}

And inside the xaml of that control, I have a TextBlock whose Foreground is Foreground="{x:Bind IsPlaying, Converter={StaticResource RowColorConverter}, ConverterParameter={Binding Theme}, Mode=OneWay}"

However, the converter doesn't seem to get the theme. It is always null.

class RowColorConverter : Windows.UI.Xaml.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return value.Equals(true) ? Helper.GetHighlightBrush() :
                                    parameter is ElementTheme && (ElementTheme)parameter == ElementTheme.Dark ? Helper.WhiteSmokeBrush : Helper.BlackBrush;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return null;
    }
}

What is wrong?

Seaky Lone
  • 992
  • 1
  • 10
  • 29

2 Answers2

0

You used new PropertyMetadata(null) when creating Theme.

Its meaning is to set the default value of Theme to null. Please try to modify it to new PropertyMetadata(ElementTheme.Light).

Update

I don't know which color to convert back when the music is not being played so I am trying this way

I try to simplify the content, assuming you don't need theme resources. This is the solution to the idea:

First define two brush resources in App.xaml, which are unplayed state and play state.

<SolidColorBrush x:Key="UnplayForeground" Color="White"/>
<SolidColorBrush x:Key="PlayingForeground" Color="Red"/>

Write a method to retrieve the resource with the specified name from the current Resource resource:

public static Brush GetResourceBrush(string key)
{
    return (Brush)Application.Current.Resources[key];
}

Then directly convert it with the IsPlaying property.

public class RowColorConverter:IValueConverter
{
    if(value is bool isPlaying)
    {
        return isPlaying ? GetResourceBrush("PlayingForeground") : 
                           GetResourceBrush("UnplayForeground");
    }
    return GetResourceBrush("UnplayForeground");
}

Update2

But how is your key different from my Theme?

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Light">
                <SolidColorBrush x:Key="PlayingForeground" Color="Green"/>
                <SolidColorBrush x:Key="UnplayForeground" Color="Black"/>
            </ResourceDictionary>
            <ResourceDictionary x:Key="Dark">
                <SolidColorBrush x:Key="PlayingForeground" Color="Red"/>
                <SolidColorBrush x:Key="UnplayForeground" Color="White"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Application.Resources>

The theme switch needs to create two resource dictionaries named Light and Dark respectively. Within these two resource dictionaries, there are resources with the same name.

When you need to change the theme, you should modify the RequestedTheme property of the Control instead of creating a new Theme property.

When the theme is switched, the software also switches the corresponding ResourceDictionary, and the name of the resource is unchanged. This is why GetResourceBrush(string key) always gets the current theme resource based on key.

Best regards.

Richard Zhang
  • 7,523
  • 1
  • 7
  • 13
  • I have changed it but it is still not working. I have actually set this value to Dark in the xaml but it is still recognized as null. – Seaky Lone Aug 30 '19 at 09:55
  • Hi, Can you provide the XAML code for your UserControl? This may be a binding issue – Richard Zhang Aug 30 '19 at 09:59
  • If needed the source xaml of the control is [here](https://github.com/SeakyLuo/SMPlayer/blob/master/SMPlayer/PlaylistControl.xaml), the place where it is used is [here](https://github.com/SeakyLuo/SMPlayer/blob/master/SMPlayer/NowPlayingFullPage.xaml), the converter is [here](https://github.com/SeakyLuo/SMPlayer/blob/master/SMPlayer/MusicConverter.cs). – Seaky Lone Aug 30 '19 at 10:00
  • Hi, I check your code and find the reason: `ConverterParameter` is not a **DependencyProperty** so it won't be able to use bindings. If you want to change the color according to the theme, you don't have to be so complicated. You can refer to the [ThemeResource related documentation](https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/resourcedictionary-and-xaml-resource-references#theme-resources-and-theme-dictionaries), just set the control's `RequestedTheme` to automatically switch the text color. – Richard Zhang Aug 30 '19 at 11:34
  • The reason why I am doing this way is I am highlighting the current playing music. So I used a converter to do it. And I don't know which color to convert back (because I have two themes) when the music is not being played so I am trying this way. Do you have any suggestions for that? – Seaky Lone Aug 30 '19 at 11:54
  • I am so sorry I updated my comment earlier and didn't let you know that. The reason why I don't know the color to switch back is that I have two themes and the text foreground is different in two themes. That's why I am doing that. – Seaky Lone Aug 30 '19 at 12:29
  • Don't worry, although you have two themes, there is only one current theme, and `Application.Current.Resources[key]` will return a resource based on the current theme selection. The premise is that you need to define resources in the `ResourceDictionary` – Richard Zhang Aug 30 '19 at 12:37
  • But how is your `key` different from my `Theme`? – Seaky Lone Aug 30 '19 at 12:43
  • I have updated my answer to the explanation of your question. In short, I suggest you modify the `RequestedThem`e property instead of creating the `Theme` property, which will help you manage your own theme resources. – Richard Zhang Aug 30 '19 at 12:59
  • I am sorry for my silliness. I followed your instructions but it seems not working? Could you take a look at my [code](https://github.com/SeakyLuo/SMPlayer/blob/master/SMPlayer/PlaylistControl.xaml.cs) again? I now change the `Theme` to be a property that controls the `RequestTheme`. I have tried setting that of my `ListView` and `TextView` to `Dark` manually but my texts are still black. [App.xaml](https://github.com/SeakyLuo/SMPlayer/blob/master/SMPlayer/App.xaml), [Converter](https://github.com/SeakyLuo/SMPlayer/blob/master/SMPlayer/MusicConverter.cs) It is set to Dark in NowPlayingFullPage – Seaky Lone Aug 30 '19 at 13:40
  • Sorry, this is my problem, I didn't make it clear. Note that `Application.Current.Resources` is used when the resource is retrieved, which means that it retrieves the theme resource of the current Application level, and the `RequestedTheme` that modifies the PlaylistControl is the Element level, and cannot change the currently referenced theme resource. However, as an application, the theme of a control is usually not modified locally. Do you have special needs for this? – Richard Zhang Aug 30 '19 at 14:41
  • Not really. This is my personal project. But I really would like to implement this because the background is black and it just looks a lot better. I am mimicking the Groove music. And the NowPlaying page of that is black. But in my project I have one more NowPlayingPage that would almost share the same `ListView` but not the `Theme`. So I extracted out the `ListView` with its content and a lot of functions for robustness. And this is the problem that I am facing. – Seaky Lone Aug 30 '19 at 14:48
  • So maybe we should go to my original method and figure out how to use `ConverterParameter` to implement what I want? – Seaky Lone Aug 30 '19 at 14:51
  • Then I really recommend that you use `ThemeResource` for resource binding.This is an efficient way to manage color resources, allowing the software to automatically replace colors based on the current theme. You don't have to use the `ConverterParameter` to pass the theme data, just select the appropriate color from the resource dictionary using the name according to the current software theme. As I said at the beginning, `ConverterParameter` can't transfer data using binding. – Richard Zhang Aug 30 '19 at 14:57
  • Thank you so much for your patient instructions. Just in case of further questions, could you please email me your WeChat or QQ to extend our discussion? My email is 464082754@qq.com. Again, your efforts are very appreciated. – Seaky Lone Aug 30 '19 at 15:32
0

Thank Richard for answering my question. As is mentioned by him, I can't pass the bound theme to the ConverterParameter. So there is no way to solve my problem using ConverterParameter.

The reason why I am posting an answer is I just want to show my workaround to get the problem done.

Since I have only one PlaylistControl displayed at the same time, I can declare another static variable UITheme CurrentTheme. When the control is Loaded, assign its member variable Theme to the CurrentTheme and I can easily switch color in the converter:

public object Convert(object value, Type targetType, object parameter, string language)
{
    return value.Equals(true) ? Helper.GetHighlightBrush() :
                                CurrentTheme == ElementTheme.Dark ? Helper.WhiteSmokeBrush : Helper.BlackBrush;
}
Seaky Lone
  • 992
  • 1
  • 10
  • 29