Sturm
Sturm

Reputation: 4125

UserControl that works with its own data

I have a user control with a TextBox (Y) and a Button, in my MainWindow(Y) there is another TextBox. When you press the Button a message pops-up and shows us the product X*Y.

Right now if I just simply insert via XAML another as it's bound to some data both UserControl, the original and the just added displays the same (because as I said the TextBox.Text is bound).

What I want is to know how could I extend this and add several UserControl in my MainWindow, so I could type different values in each UserControl and then press the Button and see how much is each product.

enter image description here

RootViewMode.cs

public class RootViewModel : INotifyPropertyChanged
    {
        #region Implementation of INotifyPropertyChanged

        private double _x;
        private double _y;

        public double X
        {
            get { return _x; }
            set
            {
                _x = value;
                OnPropertyChanged("X");
            }
        }

        public double Y
        {
            get { return _y; }
            set
            {
                _y = value;
                OnPropertyChanged("Y");
            }
        }

        public double XY
        {
            get { return _x * _y; }
        }
    }

UserControl1.xaml

  <StackPanel>
            <Label Content="Y:" />
            <TextBox Text="{Binding Path=Y, UpdateSourceTrigger=PropertyChanged, FallbackValue=1}" Margin="5" />
            <Button Content="Press me" Click="OnButtonClick" />
        </StackPanel>

UserControl1.xaml.cs

private void OnButtonClick(object sender, RoutedEventArgs e)
    {
        var viewModel = (RootViewModel)DataContext;
        var resultMessage = string.Format("{0} * {1} = {2}", viewModel.X, viewModel.Y, viewModel.XY);

        MessageBox.Show(resultMessage, "X * Y");
    }

MainWindow.xaml

 <StackPanel>
        <Label Content="X:" />
        <TextBox Text="{Binding Path=X, UpdateSourceTrigger=PropertyChanged}" Margin="5" Height="24" />
        <WpfApplication22:UserControl1  Margin="5" />
        <WpfApplication22:UserControl1  Margin="5" />
    </StackPanel>

Of course that inserting the UserControl this way I obtain not desired results. I suspect that I have to create a new RootViemModel for each UserControl, but this has to be done dynamically. I don't want just 2 UserControl but a way to generate them, maybe with a Button that says "Create UserControl!". Thanks.

(Thanks to sevenate for helping me with the code)

Upvotes: 2

Views: 88

Answers (1)

Fede
Fede

Reputation: 44038

You need an ItemsControl for that:

<Window x:Class="MiscSamples.UserControlItemsControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="UserControlItemsControl" Height="300" Width="300">
    <DockPanel>
        <StackPanel DockPanel.Dock="Top">
            <Label Content="X:"/>
            <TextBox Text="{Binding X}"/>
            <Button Content="Add User Control" Command="{Binding AddUserControlCommand}"/>
        </StackPanel>

        <ItemsControl ItemsSource="{Binding Children}">
            <ItemsControl.Template>
                <ControlTemplate>
                    <ScrollViewer CanContentScroll="True">
                        <ItemsPresenter/>
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <!-- Here you can place your local:UserControl. I just thrown the UI elements -->
                    <GroupBox Header="User Control">
                        <StackPanel>
                            <Label Content="Y:"/>
                            <TextBox Text="{Binding Y}"/>
                            <Button Content="Press Me!" Command="{Binding PressMeCommand}"/>
                        </StackPanel>
                    </GroupBox>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </DockPanel>
</Window>

Code Behind:

public partial class UserControlItemsControl : Window
{
    public UserControlItemsControl()
    {
        InitializeComponent();
        DataContext = new RootViewModel();
    }
}

RootViewModel:

public class RootViewModel: PropertyChangedBase
{
    private double _x;
    public double X
    {
        get { return _x; }
        set
        {
            _x = value;
            OnPropertyChanged("X");
        }
    }

    public ObservableCollection<UserControlViewModel> Children { get; set; }
    public Command AddUserControlCommand { get; set; }

    public RootViewModel()
    {
        Children = new ObservableCollection<UserControlViewModel>();
        AddUserControlCommand = new Command(AddUserControl);
    }

    private void AddUserControl()
    {
        var child = new UserControlViewModel();
        child.PressMeCommand = new Command(() => OnUserControlPressed(child));
        Children.Add(child);
    }

    private void OnUserControlPressed(UserControlViewModel item)
    {
        if (item != null)
        {
            var xy = X * item.Y;
            var resultMessage = string.Format("{0} * {1} = {2}", X, item.Y, xy);

            MessageBox.Show(resultMessage, "X * Y");    
        }
    }
}

UserControlViewModel:

public class UserControlViewModel:PropertyChangedBase
{
    private double _y;
    public double Y
    {
        get { return _y; }
        set
        {
            _y = value;
            OnPropertyChanged("Y");
        }
    }

    public Command PressMeCommand { get; set; }
}

Command class (To avoid using click event handlers where they don't belong):

//Dead-simple implementation of ICommand
//Serves as an abstraction of Actions performed by the user via interaction with the UI (for instance, Button Click)
public class Command : ICommand
{
    public Action Action { get; set; }

    public void Execute(object parameter)
    {
        if (Action != null)
            Action();
    }

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

    private bool _isEnabled = true;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set
        {
            _isEnabled = value;
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }

    public event EventHandler CanExecuteChanged;

    public Command(Action action)
    {
        Action = action;
    }
}

public class Command<T>: ICommand
{
    public Action<T> Action { get; set; }

    public void Execute(object parameter)
    {
        if (Action != null && parameter is T)
            Action((T)parameter);
    }

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

    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set
        {
            _isEnabled = value;
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }

    public event EventHandler CanExecuteChanged;

    public Command(Action<T> action)
    {
        Action = action;
    }
}

Result:

enter image description here

Upvotes: 3

Related Questions