简体   繁体   中英

Bind the DataContext of a UserControl to a sub ViewModel

I'm new to WPF and MVVM. Currently building a WPF application using MVVM pattern, but quickly got stuck with multiple MVVMs.

Basically I have a MainWindow, and a UserControl on the MainWindow. It's a simple UserControl which only consists of a Image Control and a Scroll Bar. As Shown below:

<UserControl x:Class="AutomaticContourEvaluation_WURO_WPF.Views.TwoDImageControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:AutomaticContourEvaluation_WURO_WPF.Views"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Image Grid.Column="0" Source="{Binding Path=CurrentSlice}" x:Name="sliceDisplayControl" />
    <ScrollBar Grid.Column="1" Minimum="0" Maximum="{Binding Path=NumberOfSlices, FallbackValue=10}" Value="{Binding Path=SliceIndex, Mode=TwoWay, FallbackValue=0}" x:Name="sliceSelectionScrollBar" Margin="0,0,0,0"/>
</Grid>

Corresponding ViewModel is designed to be:

internal class TwoDSliceViewModel : INotifyPropertyChanged
{
    private List<BitmapImage> _imageSlices;
    private int _sliceIndex = 0;

    public BitmapImage CurrentSlice { get { return _imageSlices[SliceIndex]; } }
    public int NumberOfSlices { get { return _imageSlices.Count; } }
    public int SliceIndex
    {
        get
        {
            return _sliceIndex;
        }
        set
        {
            if (_sliceIndex != value)
            {
                _sliceIndex = value;
                NotifyPropertyChanged("SliceIndex");
                NotifyPropertyChanged("CurrentSlice");
            }
        }
    }

    public TwoDSliceViewModel()
    {
        BitmapImage demoImage = new BitmapImage();
        demoImage.UriSource = new Uri("2Ddemo.jpg", UriKind.Relative);
        demoImage.CacheOption = BitmapCacheOption.OnLoad;
        if (_imageSlices == null)
        {
            _imageSlices = new List<BitmapImage>();
        }
        _imageSlices.Add(demoImage);
    }

    public TwoDSliceViewModel(List<BitmapImage> imageSlices)
    {
        _imageSlices = imageSlices;
    }

    public void updateImageSlices(List<BitmapImage> images)
    {
        _imageSlices = images;
        _sliceIndex = 0;
        NotifyPropertyChanged("CurrentSlice");
        NotifyPropertyChanged("NumberOfSlices");
        NotifyPropertyChanged("SliceIndex");
    }

    #region INotifyPropertyChanged Methods
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
}

In my MainWindowViewModel, there is an instance of the TwoDSliceViewModel:

internal class MainWindowViewModel:INotifyPropertyChanged
{
    ...
    private TwoDSliceViewModel _twoDSliceViewModel = new TwoDSliceViewModel();
    public TwoDSliceViewModel TwoDModuleViewModel { get { return _twoDSliceViewModel; } }
    ...
}

In order to get binding of the UserControl(TwoDSliceControl) work, I set up the DataContext of the UserControl in my MainWindow.xaml like below:

<Window ...>
    <Window.DataContext>
        <viewModel:MainWindowViewModel />
    </Window.DataContext>
    ...
    <control:TwoDImageControl x:Name="twoDSliceImageControl" DataContext="{Binding Path=TwoDModuleViewModel}" />
    ...
</Window>

My UserControl (TwoDSliceControl) is ViewModel specific, so I choose this way instead of using Dependency Properties. But the binding failed. You could see in my code that I created some demo data upon instantiation of the TwoDSliceViewModel, but those dummy data don't show up.

I've used breakpoints to find out that After the MainWindow is successfully initialized, the DataContext of the TwoDSliceControl is well set. But the Source property of the ImageControl in the TwoDSliceControl and the MaxValue property of the ScrollBar in the TwoDSliceControl are null.

I have the feeling that, this line of XAML code :

<control:TwoDImageControl x:Name="twoDSliceImageControl" DataContext="{Binding Path=TwoDModuleViewModel}" />

actually initialize the twoDSliceImageControl first and then set the twoDSliceImageControl.DataContext property. Upon initialization, the twoDSliceImageControl.DataContext is null, so the Binding within the twoDSliceImageControl fails. Though after initialization, the twoDSliceImageControl.DataContext is well set, bindings within the UserControl doesn't refresh, and they're still null.

Any workaround to solve this issue? Have been stuck on it for a while, and didn't find a proper solution. Thanks guys!

Thanks everyone. I've finally found it out. It seems that these lines of code have some issues:

BitmapImage demoImage = new BitmapImage();
demoImage.UriSource = new Uri("2Ddemo.jpg", UriKind.Relative);
demoImage.CacheOption = BitmapCacheOption.OnLoad;

Instead I need to wrap the BitmapImage creation around with BitmapImage.BeginInit() and BitmapImage.EndInit().

So the working code is :

BitmapImage demoImage = new BitmapImage();
demoImage.BeginInit();
demoImage.UriSource = new Uri("2Ddemo.jpg", UriKind.Relative);
demoImage.CacheOption = BitmapCacheOption.OnLoad;
demoImage.EndInit();

And there's nothing wrong with the binding actually!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM