WPF Navigation and Rotating Backgrounds

2019-09-17 21:20发布

I'm working on an application, and I'm using the MVVM approach.
Basically, there are currently two Pages, and 1 MainWindow.
I switch between the pages using a Frame inside MainWindow.

In the main window, there are 2 buttons which are basically global and should show in all pages; x (exit) and settings.
This is basically my 'shell', as I decided to not use a window border.

The problem is I'd like each page to have a different background and this is where it gets complicated:
- Settings page: Grey background.
- Main Page: Rotating background color that changes according to a property.

The thing is the background is being set in the main window, because it should apply to the global area as well (the top, where the exit and settings buttons are).

I first set the background (in MainWindow) as bound to a property the represents the current page (the value is then being translated into a color hex code with the help of a converter).

All in all, this results in a case where the background changes when a page is changed, but not when the property inside MainPage changes. I can clearly understand why, but I have no idea how to solve it.

The possible solutions I came up with so far:

  • Somehow causing the binding in MainWindow to update/refresh when the property is changed in MainPage.
  • Changing the background manually from inside each of the pages. (Although doesn't it negate the idea of mvvm?)
  • Move the background into each of the pages and set it from there, while making the global buttons on top of the page (which could be a bad thing in case controls end up overlapping).

If so, what would be the best solution to this problem?

标签: c# wpf mvvm
2条回答
在下西门庆
2楼-- · 2019-09-17 21:53

I think you should use Application Properties for storing background. There are various benefit of this :

1) Globally available 2) Easy to remember or store user preference 3) Automatically maintain separate profile for each user as it store values in AppData folder of user.

you can use Messenger to notify that background property has changed so that main window or shell could pull out new background value and update it.

查看更多
啃猪蹄的小仙女
3楼-- · 2019-09-17 21:54

If you haven't already, I'd suggest you install some package via NuGet to make MVVM style development more enjoyable. I personally prefer MVVMLight which is... well, light, but it also packs lot's of helpful features.

To communicate between ViewModels, you have (at least) two possible approaches.

1) ViewModelLocator (not recommended)

ViewModelLocator is central place holding references to all of your viewmodels. You could add a property that is then used by all of the viewmodels to get/set the background.

....
x:Name="Main"
DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}">
....
<Grid Background="{Binding Background, Converter={StaticResource StringBrushConverter}}">
...

2) Messenger (recommended)

When ever property changes in your viewmodel(s) or method is executed, you could send a message that your MainViewModel is registered to listen to. Sending a message would be as easy as...

Messenger.Default.Send(new UpdateBackgroundMessage(new SolidColorBrush(Colors.Blue)));

And you'd register for this message in your MainViewModel's constructor:

Messenger.Default.Register<UpdateBackgroundMessage>(this, message =>
    {
        Background = message.Brush;
    });

Actual message class would be:

public class UpdateBackgroundMessage : MessageBase
{
    public UpdateBackgroundMessage(Brush brush)
    {
        Brush = brush;
    }
    public Brush Brush { get; set; }
}

I know I'm simplifying things here but I hope you got the idea. Both approaches are valid even if you decide not to use MVVMLight.

Edit:

Here's Git repo with example https://github.com/mikkoviitala/cross-viewmodel-communication

imgur

查看更多
登录 后发表回答