Derrick Moeller
Derrick Moeller

Reputation: 4960

WPF: Change DataContext if Null?

I have a view that I would like to assign a "backup" viewmodel to. Essentially if "Generic" is null I would like to set the DataContext to "GenericFactory". "GenericFactory" is able to create an instance of the "Generic" viewmodel. Upon creation the viewmodel is assigned to the appropriate property and the PropertyChanged event is fired, however given the code below the only DataContext I'm ever bound to is "GenericFactory". Can anyone explain and/or offer an alternative solution?

XAML

<Page x:Class="GenericProject.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:vw="clr-namespace:GenericProject.View">
    <StackPanel>
        <!--Additional markup-->
        <vw:GenericView>
            <vw:GenericView.Style>
                <Style TargetType="{x:Type vw:GenericView}">
                    <Setter Property="DataContext" Value="{Binding Generic}" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Generic}" Value="{x:Null}">
                            <Setter Property="DataContext" Value="{Binding GenericFactory}" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </vw:GenericView.Style>
        </vw:GenericView>
    </StackPanel>
</Page>

ViewModel

public class MainPageViewModel : ViewModelBase
{
    public GenericViewModel Generic
    {
        get { return _generic; }
        private set
        {
            if (_generic != value)
            {
                _generic = value;
                base.OnPropertyChanged("Generic");
            } 
        }
    }

    public GenericFactoryViewModel GenericFactory { get; private set; }

    private void OnGenericFactoryCreatedGeneric(object sender, CreatedGenericEventArgs e)
    {
        Generic = e.Generic;
    }

    public MainPageViewModel()
    {
        GenericFactory = new GenericFactoryViewModel();
        GenericFactory.CreatedGeneric += OnGenericFactoryCreatedGeneric;
    }
}

Thanks - Derrick

Upvotes: 1

Views: 1153

Answers (2)

Derrick Moeller
Derrick Moeller

Reputation: 4960

Thanks to XAMIMAX's comment I was able to find a solution using PriorityBinding.

XAML

<Page x:Class="GenericProject.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:GenericProject"
      xmlns:vw="clr-namespace:GenericProject.View">
    <Page.Resources>
        <local:NullToDependencyPropertyUnsetConverter x:Key="NullToDependencyPropertyUnsetConverter" />
    </Page.Resources>
    <StackPanel>
        <!--Additional markup-->
        <vw:GenericView>
            <vw:GenericView.DataContext>
                <PriorityBinding>
                    <Binding Path="Generic" Converter="{StaticResource NullToDependencyPropertyUnsetConverter}" />
                    <Binding Path="GenericFactory" />
                </PriorityBinding>
            </vw:GenericView.DataContext>
        </vw:GenericView>
    </StackPanel>
</Page>

Converter

public class NullToDependencyPropertyUnsetConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value ?? DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Upvotes: 1

ndonohoe
ndonohoe

Reputation: 10240

I don't know how your factory works so this is probably not working code but you should be handling this logic in the view model, your view should just set the datacontext.

public GenericViewModel Generic
    {
        get 
        { 
            if(_generic == null)
            {
                GenericFactory.Create();
            }
            return _generic;
        }
        private set
        {
            if (_generic != value)
            {
                _generic = value;
                base.OnPropertyChanged("Generic");
            }
        }
    }

This will return null for Generic but when OnGenericFactoryCreatedGeneric is called it will set Generic and then cause the binding to update to a newly created view model.

If your factory has a synchronous create that returns the ViewModel then that would be better as Generic will never return null. _generic = GenericFactory.Create();

Upvotes: 0

Related Questions