Even though the example targets MAUI, the same technique will work for WPF too.
Short Answer is it doesn't. Normally you do not want your ViewModel to contain references to the View. It should be able to execute without a View even existing. However, I was working through one of Microsoft's MAUI walkthroughs and they were referencing color resources defined in the View.
I had tweaked the walkthrough a bit and was using a ViewModel. Normally the Color resources would be defined as properties in the ViewModel and I would bind to them. But I was curious to see how difficult it would be for the ViewModel to access resources defined in the XAML.
<Color x:Key="bgColor">#C0C0C0</Color>
<Color x:Key="fgColor">#0000AD</Color>
</ContentPage.Resources>
You define a public property in the ViewModel and have the View populate it at the appropriate times. The appropriate times are OnLoaded and OnBindingContextChanged.
Here's the property definition in the ViewModel.
public MainPage mainPage { get; set; }
public MainPage()
InitializeComponent();
this.Loaded += LinkThisToBindingContext;
(this.BindingContext as MainViewModel).mainPage = this;
}
The View's BindingContext is already populated in the View's constructor so BindingContextChanged is not normally raised after the constructor. I populate the ViewModel in the loaded event too so I have all bases covered.
Here's the XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Notes"
x:Class="Notes.MainPage">
<ContentPage.BindingContext>
<local:MainViewModel/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<Color x:Key="bgColor">#C0C0C0</Color>
<Color x:Key="fgColor">#0000AD</Color>
</ContentPage.Resources>
<Grid RowDefinitions="Auto"
Background="{DynamicResource bgColor}"
ColumnDefinitions="*,*"
Padding="40">
<Button Margin="5" Text="Dark" Grid.Row="0" Grid.Column="0"
Command="{Binding DarkCommand}"
TextColor="{DynamicResource fgColor}"
BorderColor="{DynamicResource fgColor}"
BorderWidth="1"
BackgroundColor="{DynamicResource bgColor}"/>
<Button Margin="5" Text="Light" Grid.Row="0" Grid.Column="1"
Command="{Binding LightCommand}"
TextColor="{DynamicResource fgColor}"
BorderColor="{DynamicResource fgColor}"
BorderWidth="1"
BackgroundColor="{DynamicResource bgColor}"/>
</Grid>
</ContentPage>
Here is the code behind...
public partial class MainPage : ContentPage
public MainPage()
InitializeComponent();
this.Loaded += LinkThisToBindingContext;
private void LinkThisToBindingContext(object sender, EventArgs e)
(this.BindingContext as MainViewModel).mainPage = this;
}
and here is the ViewModel.
namespace Notes
partial class MainViewModel: ObservableObject
public MainPage mainPage { get; set; }
[RelayCommand]
public void Dark()
mainPage.Resources["fgColor"] = Colors.Navy;
[RelayCommand]
public void Light()
mainPage.Resources["fgColor"] = Colors.Silver;
}
}
If you run the application you can see the relay commands have access to the resources via the mainPage property. It's really bad architecture, but it can be done.
No comments:
Post a Comment