VivekDev
VivekDev

Reputation: 25543

WPF Usercontrol databinding with MVVM ViewModel

I have spent quite some time on this so seeking help.

Simple data binding with usercontrol with mvvm light does not work.

I have done the following.

  1. Created a MvvmLight (WPF451) project using VS 2015 and named it WpfDataBindingUserControlT1
  2. Added a UserControl and renamed it to SimpleUserControl.xaml
  3. Added some lables as a children(wraped in stackpanel) to the grid inside SimpleUserControl.xaml(All code is given below)
  4. Added a dependency properties in the code behind of the SimpleUserControl.xaml(SimpleUserControl.cs) so that these will help me in databinding.

The data binding simply does not work. I have pulled half of my hair on this so please help. I guess I am missing very simple on this.

The code is as follows.

  1. MainWindows.xaml

    <Window x:Class="WpfDataBindingUserControlT1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:ignore="http://www.galasoft.ch/ignore"
            xmlns:local="clr-namespace:WpfDataBindingUserControlT1"
            mc:Ignorable="d ignore"
            Height="400"
            Width="300"
            Title="MVVM Light Application"
            DataContext="{Binding Main, Source={StaticResource Locator}}">
    
        <Window.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Skins/MainSkin.xaml" />
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Window.Resources>
    
        <Grid x:Name="LayoutRoot">
    
            <TextBlock FontSize="36"
                       FontWeight="Bold"
                       Foreground="Purple"
                       Text="{Binding WelcomeTitle}"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center"
                       TextWrapping="Wrap" />
    
            <local:SimpleUserControl DataContext="{Binding RelativeSource={RelativeSource Self}}" CellValue="{Binding WelcomeTitle}" />
        </Grid>
    </Window>
    
  2. MainWindow.cs ( I did not change any thing in this file.)

    using System.Windows;
    using WpfDataBindingUserControlT1.ViewModel;
    
    namespace WpfDataBindingUserControlT1
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            /// <summary>
            /// Initializes a new instance of the MainWindow class.
            /// </summary>
            public MainWindow()
            {
                InitializeComponent();
                Closing += (s, e) => ViewModelLocator.Cleanup();
            }
        }
    }
    
  3. SimpleUserControl.xaml

    <UserControl x:Class="WpfDataBindingUserControlT1.SimpleUserControl"
                 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:WpfDataBindingUserControlT1"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid>
            <StackPanel>
                <Label Content="This Prints" />
                <Label Name="MyLable" Content="{Binding Path=CellValue}"></Label>
                <Label Content="This also Prints" />
            </StackPanel>
        </Grid>
    </UserControl>
    
  4. SimpleUserControl.cs (added a dependency prop)

    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfDataBindingUserControlT1
    {
        /// <summary>
        /// Interaction logic for SimpleUserControl.xaml
        /// </summary>
        public partial class SimpleUserControl : UserControl
        {
            public string CellValue
            {
                get { return (string)GetValue(CellValueProperty); }
                set { SetValue(CellValueProperty, value); }
            }
    
            public static readonly DependencyProperty CellValueProperty = 
                DependencyProperty.Register("CellValue", typeof(string), typeof(SimpleUserControl), new FrameworkPropertyMetadata
                {
                    BindsTwoWayByDefault = true,
                });
    
            public SimpleUserControl()
            {
                InitializeComponent();
            }
        }
    }
    
  5. MainViewModel.cs (I have not changed any thing in this)

    using GalaSoft.MvvmLight;
    using WpfDataBindingUserControlT1.Model;
    
    namespace WpfDataBindingUserControlT1.ViewModel
    {
        /// <summary>
        /// This class contains properties that the main View can data bind to.
        /// <para>
        /// See http://www.mvvmlight.net
        /// </para>
        /// </summary>
        public class MainViewModel : ViewModelBase
        {
            private readonly IDataService _dataService;
    
            /// <summary>
            /// The <see cref="WelcomeTitle" /> property's name.
            /// </summary>
            public const string WelcomeTitlePropertyName = "WelcomeTitle";
    
            private string _welcomeTitle = string.Empty;
    
            /// <summary>
            /// Gets the WelcomeTitle property.
            /// Changes to that property's value raise the PropertyChanged event. 
            /// </summary>
            public string WelcomeTitle
            {
                get
                {
                    return _welcomeTitle;
                }
                set
                {
                    Set(ref _welcomeTitle, value);
                }
            }
    
            /// <summary>
            /// Initializes a new instance of the MainViewModel class.
            /// </summary>
            public MainViewModel(IDataService dataService)
            {
                _dataService = dataService;
                _dataService.GetData(
                    (item, error) =>
                    {
                        if (error != null)
                        {
                            // Report error here
                            return;
                        }
    
                        WelcomeTitle = item.Title;
                    });
            }
    
            ////public override void Cleanup()
            ////{
            ////    // Clean up if needed
    
            ////    base.Cleanup();
            ////}
        }
    }
    
  6. ViewModelLocator.cs (I have not changed any thing in this as well.)

    /*
      In App.xaml:
      <Application.Resources>
          <vm:ViewModelLocatorTemplate xmlns:vm="clr-namespace:WpfDataBindingUserControlT1.ViewModel"
                                       x:Key="Locator" />
      </Application.Resources>
    
      In the View:
      DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
    */
    
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Ioc;
    using Microsoft.Practices.ServiceLocation;
    using WpfDataBindingUserControlT1.Model;
    
    namespace WpfDataBindingUserControlT1.ViewModel
    {
        /// <summary>
        /// This class contains static references to all the view models in the
        /// application and provides an entry point for the bindings.
        /// <para>
        /// See http://www.mvvmlight.net
        /// </para>
        /// </summary>
        public class ViewModelLocator
        {
            static ViewModelLocator()
            {
                ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    
                if (ViewModelBase.IsInDesignModeStatic)
                {
                    SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
                }
                else
                {
                    SimpleIoc.Default.Register<IDataService, DataService>();
                }
    
                SimpleIoc.Default.Register<MainViewModel>();
            }
    
            /// <summary>
            /// Gets the Main property.
            /// </summary>
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
                "CA1822:MarkMembersAsStatic",
                Justification = "This non-static member is needed for data binding purposes.")]
            public MainViewModel Main
            {
                get
                {
                    return ServiceLocator.Current.GetInstance<MainViewModel>();
                }
            }
    
            /// <summary>
            /// Cleans up all the resources.
            /// </summary>
            public static void Cleanup()
            {
            }
        }
    }
    

Upvotes: 2

Views: 4169

Answers (1)

SWilko
SWilko

Reputation: 3612

Add this line to your SimpleUserControl.cs constructor

public SimpleUserControl()
{
    InitializeComponent();

    (this.Content as FrameworkElement).DataContext = this;
}    

You're basically setting the DataContext of the first element in the UserControl.

Jerry Nixon has a great article on this here

UPDATE forgot to add get rid of the RelativeSource eg

<local:SimpleUserControl CellValue="{Binding WelcomeTitle}" />

Upvotes: 4

Related Questions