Search code examples
c#wpfmvvmuser-controls

Get a value from a user control


I have a User control with a button. when click on the button create a random number. I want use this number in Main Window. I use user control for 3 time in my Main Window. but I can't get the number in main window. I use MVVM. Please help me!

UserControl.xaml

    <UserControl x:Class="test.UserControl1"
             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:test"
             mc:Ignorable="d" 
             d:DesignHeight="100" d:DesignWidth="300">
    <Grid>
        <Grid.DataContext>
            <local:UserControl1VM/>
        </Grid.DataContext>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="{Binding Path=txt}"/>
        <Button Grid.Column="1" Content="Show Num" Command="{Binding Path=AddNum}"/>
    </Grid>
</UserControl>

User Contorl View Model

public class UserControl1VM : INotifyPropertyChanged
{
    private string _txt;

    public string txt
    {
        get { return _txt; }
        set
        {
            _txt = value;
            OnPropertyChanged("txt");
        }
    }
    public RelayCommand AddNum { get; set; }


    public UserControl1VM()
    {
        AddNum = new RelayCommand(DoAddNum);
    }

    private void DoAddNum(object obj)
    {
        Random random = new Random();
        txt = random.Next(0, 100).ToString();
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

MainWindow.xaml

<Window x:Class="test.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:test"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="4">
    <local:UserControl1 Margin="20"/>
    <local:UserControl1 Margin="20"/>
    <local:UserControl1 Margin="20"/>
    <UniformGrid Columns="2" Margin="20">
        <TextBlock Text="{Binding Path=Sum}"/>
        <Button Content="Sum" Command="{Binding Path=AddSum}"/>
    </UniformGrid>
</UniformGrid>

Main Window View Model

public class MainWindowVM : INotifyPropertyChanged
{
    private string _Sum;

    public string Sum
    {
        get { return _Sum; }
        set
        {
            _Sum = value;
            OnPropertyChanged("Sum");
        }
    }

    public RelayCommand AddSum { get; set; }

    public MainWindowVM()
    {
        AddSum = new RelayCommand(DoAddSum);
    }


    private void DoAddSum(object obj)
    {

    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

RelayCommand.cs

public class RelayCommand : ICommand
{
    private string v;

    private Action<object> MyAction { get; set; }
    public RelayCommand(Action<object> MyAction)
    {
        this.MyAction = MyAction;
    }

    public RelayCommand(string v)
    {
        this.v = v;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        MyAction(parameter);
    }
}

I want When click on "Sum" show Sum of 3 numbers in the text block.


Solution

  • My suggestions

    • change 'sum' and 'txt' property types to int - sum up is easier on numbers ;-)

    • use a composite pattern on your ViewModels. Let the MainWindow view model consist of three UserControlVM's.

    Changes

    1.) Changes to MainWindow.xaml

    <UniformGrid Rows="4">
        <UniformGrid.DataContext>
            <local:MainWindowVM/>
        </UniformGrid.DataContext>
        <local:UserControl1 Margin="20" DataContext="{Binding UserControlVM1}"/>
        <local:UserControl1 Margin="20" DataContext="{Binding UserControlVM2}"/>
        <local:UserControl1 Margin="20" DataContext="{Binding UserControlVM3}"/>
    

    2.) Remove from UserControl1.xaml:

    <Grid.DataContext>
            <local:UserControl1VM/>
    </Grid.DataContext>
    

    3.) Change MainWindowVM.cs

        public class MainWindowVM : INotifyPropertyChanged
    {
        private readonly List<UserControl1VM> childVms;
    
        public UserControl1VM UserControlVM1 { get; set; }
        public UserControl1VM UserControlVM2 { get; set; }
        public UserControl1VM UserControlVM3 { get; set; }
    
        public MainWindowVM()
        {
            childVms = new List<UserControl1VM>()
            {
                new UserControl1VM(),
                new UserControl1VM(),
                new UserControl1VM(),
            };
    
            UserControlVM1 = childVms[0];
            UserControlVM2 = childVms[1];
            UserControlVM3 = childVms[2];
    
            AddSum = new RelayCommand(DoAddSum);
        }
    
        private int _Sum;
    
    
        public int Sum
        {
            get { return _Sum; }
            set
            {
                _Sum = value;
                OnPropertyChanged("Sum");
            }
        }
    
        public RelayCommand AddSum { get; set; }
    
    
        private void DoAddSum(object obj)
        {
            childVms.ForEach(vm => Sum += vm.txt);
        }
    
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }