How can I access a vm object declared in main window in Wpf Page

Babu R 81 Reputation points
2020-05-30T11:47:51.4+00:00

Hai

I have declared an object of my view model in my main window. I want to know how to access this in my xaml

my code is

Main window

      private void Button_Click(object sender, RoutedEventArgs e)
            {
                ViewModel VM = new ViewModel();
                SubPage Sp = new SubPage();
                Sp.DataContext = VM;
                MainFrame.Content = Sp;
            }

in page

<MyControls:IGrid.RowValidationRules>
                    <local:ViewModel ValidationStep="UpdatedValue"/>
                </MyControls:IGrid.RowValidationRules>

some thing like this

please help me how to access the main window object.

Thanks

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,783 questions
0 comments No comments
{count} votes

Accepted answer
  1. DaisyTian-1203 11,626 Reputation points
    2020-06-02T03:24:29.987+00:00

    1.Delete the part which related to validation in Model.cs, then you update the ViewModel.cs like the below:

    public ObservableCollection<Model> viewModels { get; set; }
    
            public ViewModel()
            {
                viewModels = new ObservableCollection<Model>()
                {
                new Model {Name="Name1",Height=170,Age=10}
                 };
            }
    

    2.Update the SubPage.xaml like below,the DataGrid access data from VM which declared in MainWindow.xaml.cs

    <Grid>
            <DataGrid ItemsSource="{Binding viewModels}" AutoGenerateColumns="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}"></DataGridTextColumn>
                    <DataGridTextColumn Header="Age" Binding="{Binding Age}"></DataGridTextColumn>
                    <DataGridTextColumn Header="Height" Binding="{Binding Height}"></DataGridTextColumn>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    
    0 comments No comments

6 additional answers

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,326 Reputation points
    2020-06-02T07:00:22.733+00:00

    Hi, if you want to use ViewModel or properties of ViewModel in ValidationRules you must add DependencyPropety to ValidationRule. Since ValidationRule does not inherit from DependencyObject you cannot create a DependecyProperty in your custom validation class. You can have a normal property in your validation class which is of a type that inherits from DependecyObject and create a DependencyProperty in that class. The following ValidationRule check the length of "M_Name" property in data item of row.

      [ContentProperty("MaxLength")]
      public class ValidateRow : ValidationRule
      {
        public MappingValue MaxLength { get; set; }
    
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
          BindingGroup bg = value as BindingGroup;
          if (bg == null && bg.Items.Count != 1) return new ValidationResult(false, "invalid use ValidateRow");
          Student s = bg.Items[0] as Student;
          if (s == null) return new ValidationResult(false, "invalid use ValidateRow");
          if (s.M_Name.Length> (int)MaxLength.Value) return new ValidationResult(false, "invalid length");
          return ValidationResult.ValidResult;
        }
      }
    

    MappingValue is a simple class that inherits from DependencyObject and has a DependencyProperty:

      public class MappingValue : DependencyObject
      {
        public object Value
        {
          get { return this.GetValue(ValueProperty); }
          set { this.SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            nameof(Value),
            typeof(object),
            typeof(MappingValue),
            new PropertyMetadata(default(int), OnValueChanged));
    
        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
          MappingValue comparisonValue = (MappingValue)d;
          BindingExpressionBase bindingExpressionBase = 
            BindingOperations.GetBindingExpressionBase(comparisonValue, BindingToTriggerProperty);
          bindingExpressionBase?.UpdateSource();
        }
    
        public object BindingToTrigger
        {
          get { return this.GetValue(BindingToTriggerProperty); }
          set { this.SetValue(BindingToTriggerProperty, value); }
        }
        public static readonly DependencyProperty BindingToTriggerProperty = DependencyProperty.Register(
            nameof(BindingToTrigger),
            typeof(object),
            typeof(MappingValue),
            new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
      }
    

    The binding does not work correctly since the ValidationRules is not part of visual tree and therefore cannot get the bound property correctly. You can use a proxy class:

      public class BindingProxy : Freezable
      {
        protected override Freezable CreateInstanceCore() => new BindingProxy();
    
        public object Data
        {
          get { return this.GetValue(DataProperty); }
          set { this.SetValue(DataProperty, value); }
        }
        public static readonly DependencyProperty DataProperty = 
          DependencyProperty.Register(nameof(Data), typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
      }
    

    In XAML you can use this BindingProxy in StaticResource lik in following demo. I insert a TextBox for MaxLength (property in ViewModel).

      <Page.Resources>
        <CollectionViewSource x:Key="GenderList" Source="{Binding NameList}" />
        <local:BindingProxy x:Key="Proxy" Data="{Binding MaxLength}"/>
      </Page.Resources>
      <Grid x:Name="grd">
        <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right" Width="200">
          <Label Content="Max. Length of Name"/>
          <TextBox x:Name="tb" Text="{Binding MaxLength}" Width="50"/>
        </StackPanel>
        <local:DataGridUser x:Name="Dg_Student" 
                            ItemsSource="{Binding Vm_StudentCollection}"  
                            CurrentItem="{Binding Vm_StudentCollection_Curitem,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"  
                            AutoGenerateColumns="False"
                            HorizontalAlignment="Left"
                            Height="282"
                            Margin="107,58,0,0" 
                            VerticalAlignment="Top" 
                            Width="247" 
                            SelectionUnit="CellOrRowHeader"
                            SelectionMode="Single">
          <local:DataGridUser.Columns>
            <DataGridTextColumn Header="Name" 
                                Binding="{Binding M_Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"  />
            <DataGridComboBoxColumn Header="Gender"  
                                    ItemsSource="{Binding Source={StaticResource GenderList}}"
                                    DisplayMemberPath="Name" 
                                    SelectedValuePath="ID"
                                    SelectedValueBinding="{Binding M_GenderID}">
            </DataGridComboBoxColumn>
            <DataGridTextColumn Header="Mark1" 
                                Binding="{Binding M_Mark1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
            <DataGridTextColumn Header="Total" 
                                Binding="{Binding M_Total, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"  />
          </local:DataGridUser.Columns>
          <local:DataGridUser.RowValidationRules>
            <local:ValidateRow ValidationStep="UpdatedValue">
              <local:MappingValue Value="{Binding Data, Source={StaticResource Proxy}}"/>
            </local:ValidateRow>
          </local:DataGridUser.RowValidationRules>
        </local:DataGridUser>
      </Grid>
    
    1 person found this answer helpful.

  2. DaisyTian-1203 11,626 Reputation points
    2020-06-01T06:03:44.937+00:00

    Welcome to our Microsoft Q&A platform!

    Your description make me little confused, I will show you my sample for Validation which use MVVM.

    NotifyObject.cs

    public class NotifyObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged;
    
                  public void RaisePropertyChanged(string propertyName)
                     {
                         if (PropertyChanged != null)
                         {
                             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                         }
                     }
    
                 }
    

    Model.cs

    public class Model : NotifyObject, IDataErrorInfo
        {
            private string name;
            public string Name
            {
                get
                {
                    return name;
                }
                set
                {
                    name = value;
                    RaisePropertyChanged("Name");
                }
            }
    
            private decimal height;
            public decimal Height
            {
                get
                {
                    return height;
                }
                set
                {
                    height = value;
                    RaisePropertyChanged("Height");
                }
            }
    
            private int age;
            public int Age
            {
                get
                {
                    return age;
                }
                set
                {
                    age = value;
                    RaisePropertyChanged("Age");
                }
            }
    
            public string Error
            {
                get { throw new NotImplementedException(); }
            }
    
            public string this[string columnName]
            {
                get
                {
                    string result = string.Empty;
                    switch (columnName)
                    {
                        case "Name": if (string.IsNullOrEmpty(Name)) result = "Name is required!"; break;
                        case "Height": if ((Height < 52) || (Height > 250)) result = "Height must be between 52 and 250"; break;
                        case "Age": if ((Age < 1) || (Age > 100)) result = "Age must be between 1 and 100"; break;
                    };
                    return result;
                }
            }
        }
    

    ViewModel.cs

    public class ViewModel : NotifyObject { public Model validModel { get; set; }
    
             public ViewModel()
             {
                 validModel = new Model();
             }
         }
    

    SubPage.xaml

     <Page.Resources>
             <Style TargetType="TextBox">
                 <Style.Triggers>
                     <Trigger Property="Validation.HasError" Value="true">
                         <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" />
                     </Trigger>
                 </Style.Triggers>
             </Style>
    
             <Style TargetType="ToolTip">
                 <Setter Property="Foreground" Value="Red"></Setter>
                 <Setter Property="HorizontalAlignment" Value="Center"></Setter>
                 <Setter Property="Background" Value="Transparent"></Setter>
                 <Setter Property="BorderThickness" Value="0"></Setter>
             </Style>
    
    
    
         </Page.Resources>
    
         <Grid >
             <Grid.RowDefinitions>
                 <RowDefinition Height="40" />
                 <RowDefinition Height="40" />
                 <RowDefinition Height="40" />
                 <RowDefinition Height="259*" />
             </Grid.RowDefinitions>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="140*" />
                 <ColumnDefinition Width="308*" />
             </Grid.ColumnDefinitions>
             <Label Grid.Row="0"
                    Height="28"
                    Margin="0,0,10,0"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Center"
                    Content="Name:" />
             <Label Grid.Row="1"
                    Height="28"
                    Margin="0,0,10,0"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Center"
                    Content="Height(cm):" />
             <Label Grid.Row="2"
                    Height="28"
                    Margin="0,0,10,0"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Center"
                    Content="Age(Integer):" />
    
    
             <TextBox Name="txtName"
                      Grid.Row="0"
                      Grid.Column="1"
                      Width="200"
                      HorizontalAlignment="Left"
                      VerticalAlignment="Center"
                      Text="{Binding validModel.Name, ValidatesOnDataErrors=True}" />
    
             <TextBox Name="txtHeight"
                      Grid.Row="1"
                      Grid.Column="1"
                      Width="200"
                      HorizontalAlignment="Left"
                      VerticalAlignment="Center"
                      Text="{Binding validModel.Height, ValidatesOnDataErrors=True}" />
    
             <TextBox Name="txtAge"
                      Grid.Row="2"
                      Grid.Column="1"
                      Width="200"
                      HorizontalAlignment="Left"
                      VerticalAlignment="Center"
                      Text="{Binding validModel.Age, ValidatesOnDataErrors=True}" />
         </Grid>
    

    MainWindow.xaml

      <Grid>
             <Frame Name="MainFrame"></Frame>
      </Grid>
    

    MainWindow.xaml.cs

    public MainWindow()
             {
                 InitializeComponent();
                 ViewModel VM = new ViewModel();
                 SubPage Sp = new SubPage();
                 Sp.DataContext = VM;
                 MainFrame.Content = Sp;
             }
    
    0 comments No comments

  3. Babu R 81 Reputation points
    2020-06-01T11:14:43.15+00:00

    This is not I want (validation)
    I want to know how to access the same object declared in main window (VM) button click in my page XAML.

    Thanks.

    0 comments No comments

  4. Peter Fleischer (former MVP) 19,326 Reputation points
    2020-06-10T05:32:45.623+00:00

    Hi, the logic of your code is unclear to me. I have revised your code, deleted all code-behind (except InitializeComponent) and concentrated all logic in a ViewModel. A better approach would be to use IDataError in StudentModel.

    <Window x:Class="Experiment.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:local="clr-namespace:Experiment"
            mc:Ignorable="d"
            Title="MainWindow" Height="800" Width="1200" Background="blue" WindowStartupLocation="CenterScreen" WindowState="Maximized">
      <Window.Resources>
        <local:ViewModel x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.RowDefinitions>
          <RowDefinition Height="1*"/>
          <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Frame x:Name="Frame1" Content="{Binding Frame1}" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Top" NavigationUIVisibility="Hidden"/>
        <Frame x:Name="Frame2" Content="{Binding Frame2}" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Top" NavigationUIVisibility="Hidden"/>
      </Grid>
    </Window>
    

    <Page
          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:Experiment" x:Class="Experiment.Page1" 
          mc:Ignorable="d" Height="400" Width="1600" Title="Page1" >
      <Grid Background="#FF5D5B5B">
        <local:DGrid x:Name="dGrid" ItemsSource="{Binding Vm_StudentCollectionSub}" RowHeaderWidth="20" HorizontalAlignment="Left" Margin="278,107,0,0" VerticalAlignment="Top" Height="145" Width="517" AutoGenerateColumns="False" SelectionUnit="CellOrRowHeader" SelectionMode="Single">
          <local:DGrid.RowValidationRules>
            <local:RowValidation ValidationStep="UpdatedValue"/>
            <!--Here I want to add step validation-->
            <!--<  ValidationStep="UpdatedValue"/>-->
          </local:DGrid.RowValidationRules>
          <local:DGrid.Columns>
            <DataGridTextColumn Header="Name" Width="100" Binding="{Binding M_Name}"/>
            <DataGridTextColumn Header="Maths" Width="100" Binding="{Binding M_Maths}"/>
            <DataGridTextColumn Header="Science" Width="100" Binding="{Binding M_Science}"/>
            <DataGridTextColumn Header="English" Width="100" Binding="{Binding M_English}"/>
          </local:DGrid.Columns>
        </local:DGrid>
        <Button Content="Add" Command="{Binding Cmd}" Margin="716,257,805,112"/>
      </Grid>
    </Page>
    

    <Page x:Class="Experiment.Page2"
          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:Experiment"
          mc:Ignorable="d" Height="400" Width="1600"
          Title="Page2">
        <Grid>
            <local:DGrid x:Name="dGrid" ItemsSource="{Binding Vm_StudentCollection}" VerticalAlignment="Top" Height="222" HorizontalAlignment="Left" Width="522" AutoGenerateColumns="False" SelectionUnit="CellOrRowHeader" SelectionMode="Single" RowHeight="22" RowHeaderWidth="15">
            <local:DGrid.Columns>
                    <DataGridTextColumn Header="Name" Width="100" Binding="{Binding M_Name}"/>
                    <DataGridTextColumn Header="Maths" Width="100" Binding="{Binding M_Maths}"/>
                    <DataGridTextColumn Header="Science" Width="100" Binding="{Binding M_Science}"/>
                    <DataGridTextColumn Header="English" Width="100" Binding="{Binding M_English}"/>
                    <DataGridTextColumn Header="Total" Width="100" Binding="{Binding M_Total}"/>
                </local:DGrid.Columns>
            </local:DGrid>
        </Grid>
    </Page>
    

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Input;
    
    namespace Experiment
    {
      public class ViewModel : RowValidation, INotifyPropertyChanged
      {
        public ViewModel()
        {
          StudentCollection.Add(new StudentModel { M_STId = 1, M_Name = "Santhosh", M_Maths = 70, M_Science = 56, M_English = 45 });
          StudentCollection.Add(new StudentModel { M_STId = 2, M_Name = "Mathew", M_Maths = 60, M_Science = 65, M_English = 77 });
          StudentCollection.Add(new StudentModel { M_STId = 3, M_Name = "Poly", M_Maths = 40, M_Science = 66, M_English = 45 });
          StudentCollection.Add(new StudentModel { M_STId = 4, M_Name = "Tony", M_Maths = 20, M_Science = 88, M_English = 49 });
          //
          cvsStudentCollection.Source = StudentCollection;
          cvsStudentCollection.View.CurrentChanged += StudentCollection_CurrentChanged;
          //
          cvsStudentCollectionSub.Source = StudentCollectionSub;
          //
          Frame1 = new Page1() { DataContext = this };
          Frame2 = new Page2() { DataContext = this };
        }
    
        public Page Frame1 { get; set; }
        public Page Frame2 { get; set; }
    
        private ObservableCollection<StudentModel> StudentCollection { get; set; } = new ObservableCollection<StudentModel>();
        private ObservableCollection<StudentModel> StudentCollectionSub { get; set; } = new ObservableCollection<StudentModel>();
    
        private CollectionViewSource cvsStudentCollection = new CollectionViewSource();
        private CollectionViewSource cvsStudentCollectionSub = new CollectionViewSource();
    
        public ICollectionView Vm_StudentCollection { get => cvsStudentCollection.View; }
        public ICollectionView Vm_StudentCollectionSub { get => cvsStudentCollectionSub.View; }
    
        private void StudentCollection_CurrentChanged(object sender, System.EventArgs e)
        {
          if (Vm_StudentCollection.CurrentItem != null)
          {
            StudentCollectionSub.Clear();
            StudentCollectionSub.Add(((StudentModel)Vm_StudentCollection.CurrentItem).Copy());
          }
        }
    
        public ICommand Cmd { get => new RelayCommand(CmdExec); }
    
        private void CmdExec(object obj)
        {
          for (int i = StudentCollectionSub.Count-1; i >= 0; i--)
          {
            var s = StudentCollectionSub[i]; ;
            StudentCollection.Add(s);
            StudentCollectionSub.Remove(s);
          }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
    }
    

    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    
    namespace Experiment
    {
      public class StudentModel : INotifyPropertyChanged
      {
        public StudentModel() { }
        public int M_STId { get; set; }
        public string M_Name { get; set; }
        public int M_Maths { get; set; }
        public int M_Science { get; set; }
        public int M_English { get; set; }
        public int M_Total { get; set; }
    
        public StudentModel Copy() => new StudentModel()
        {
          M_English = this.M_English,
          M_Maths = this.M_Maths,
          M_Name = this.M_Name,
          M_Science = this.M_Science,
          M_STId = this.M_STId,
          M_Total = this.M_Total
        };      
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
    }
    

    using System;
    using System.Windows.Input;
    
    namespace Experiment
    {
      public class RelayCommand : ICommand
      {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _action;
        public RelayCommand(Action<object> action) { _action = action; _canExecute = null; }
        public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
        public void Execute(object o) => _action(o);
        public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
        public event EventHandler CanExecuteChanged
        {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
        }
      }
    }
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.