Ravior Ravior - 1 month ago 17
C# Question

WPF MVVM Exception when Registering UserControl in DataContext

Im new to MVVM and try to follow all the guidelines I find to respect it. I would like to have a Busy-Animation on one of my usercontrols. I want to include it on the control like this.



The Usercontrol it is nested in is shown on the MainWindow using a DataTemplate for a ViewModel, for example like so:

<Window.Resources>
<DataTemplate DataType="{x:Type AppViews:AppConfigViewModel}">
<local:AppConfigView />
</DataTemplate>
</Window.Resources>

<Grid>
<ContentControl Content="{Binding CurrentPageViewModel}" />
</Grid>


When running this, the Application is shown and I also see the view for the AppConfigViewModel which is bind correctly since underlying values are displayed correctly in the view.

Now I tried to register the Busy-Animation in the ViewModel (to control it from there) by doing this in the Constructor of the BusyAnimation:

(DataContext as PageViewModel).BusyAnim = this;


For some reason the DataContext is always null and the result of this line is an exception. What am I doing wrong here?

Answer

What I tried to did there is against the idea of MVVM. I tried downcasting an object that is meant to be general.

A better aproach for the task I tried to achieve is implementing dependency properties in the busy animation component. Those are meant to be bound to from the viewmodel of the mainly displayed view. that way the busy animation can be shown when some property in the viewmodel changes. That could be for example a bool with the name "working".

this is the dependency property code in my busy animation:

public static readonly DependencyProperty ShowBusyProperty = DependencyProperty.Register("ShowBusy", typeof(Boolean), typeof(FortschrittView), new PropertyMetadata(false, OnShowBusyPropertyChanged));
public Boolean ShowBusy
{
    get { return (Boolean)this.GetValue(ShowBusyProperty); }
    set { this.SetValue(ShowBusyProperty, value); }
}

private static void OnShowBusyPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
    FortschrittView myUserControl = dependencyObject as FortschrittView;
    myUserControl.OnPropertyChanged("ShowBusy");
    myUserControl.OnShowBusyPropertyChanged(e);
}
private void OnShowBusyPropertyChanged(DependencyPropertyChangedEventArgs e)
{
    if(ShowBusy)
    {
        Start();
    }
    else
    {
        Stop();
    }
}

Yes its a lot of code, but I feel wpf wants it that way. Remember above code is in the busy-animation user control and triggers Start() Stop() functions which control storyboards.

Below xaml is in the control that uses the busyanimation, binding it to a viewmodel that the busy-animation should indicate background-work for:

 <local:BusyAnimation ShowBusy="{Binding Model.IsBusy}"/>

That ShowBusy Property there is the Dependency Property implemented above. Of course IsBusy from the model should follow the observable pattern for everything to work.

/ps: I throughoutly documented the mistakes i did and how i solved them. Can I get rid of the negative points I got somehow for creating this question?

Comments