2

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!

S.Jiang
  • 21
  • 4
  • Your code should work at first glance. How do you assign MainWindowViewModel to the DataContext of your window, that contains control:TwoDImageControl? – Anton Danylov Mar 23 '16 at 23:24
  • @AntonDanylov I've updated my post to reflect the assignment of MainWindowViewModel. – S.Jiang Mar 24 '16 at 02:29
  • Add the diagnostics from this post http://stackoverflow.com/questions/337023/how-to-detect-broken-wpf-data-binding to your DataContext binding and it will tell you how it is looking up the values and what the values are. – user3690202 Mar 24 '16 at 02:52
  • @S.Jiang: Looks quite correct, binding errors would help indeed. – H.B. Mar 24 '16 at 05:06

1 Answers1

0

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!

Ani Menon
  • 27,209
  • 16
  • 105
  • 126
S.Jiang
  • 21
  • 4