Click here to Skip to main content
15,122,559 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I haven't specified what happens when you hit the copy button or when you hit the clone button, and frankly I don't quite know how to do it.
I need to create a wpf application with 2 buttons in a homework. one of the buttons is "copy" and the other is "clone". When i press the copy button, it should copy the information in the textboxes on the screen and create a new textbox and write it into it. When we press the clone button, it should copy the information in the same way and write it into a new textbox, but this time if I try to change the value in the cloned textboxes, it should change in all value in cloned textboxes.

What I have tried:

public class MainViewModel : ReactiveObject
   {
       private CopyCloneViewModel[] _items;

       public CopyCloneViewModel[] Items
       {
           get => _items;
           set => this.RaiseAndSetIfChanged(ref _items, value);
       }

       public void AddCopy()
       {
           CopyCloneViewModel copyCloneViewModel = new CopyCloneViewModel(this);
           var item = copyCloneViewModel;

           if (this.Items == null)
           {
               return;
           }

       }
       public void AddClone()
       {
           CopyCloneViewModel copyCloneViewModel = new CopyCloneViewModel(this);
           var item = copyCloneViewModel;

           if (Items == null)
           {
               return;
           }
           Items.Add(Items);
       }

//and this is another viewmodel for copy and clone commands.

public class CopyCloneViewModel: ReactiveObject
    {
        private MainViewModel _main;

        public CopyCloneViewModel(MainViewModel mainViewModel)
        {
            _main = mainViewModel;
        }


        private void OnCopy()
        {
            _main.AddCopy();   
        }
        private void OnClone()
        {
            _main.AddCopy();
        }
        private ReactiveCommand<Unit, Unit> _addCopyCommand;
        public ReactiveCommand<Unit, Unit> AddCopyCommand => _addCopyCommand ??= ReactiveCommand.Create<Unit, Unit>((unit) =>
        {
            OnCopy();
            return Unit.Default;
        });

       private   ReactiveCommand<Unit, Unit> _addCloneCommand;
       public ReactiveCommand<Unit, Unit> AddCloneCommand => _addCloneCommand ??= ReactiveCommand.Create<Unit, Unit>((unit) =>
        {
            OnClone();
            return Unit.Default;
        });
    }

this is mainview.By adding another view to the mainview, I aimed to open a new text box every time I press the button(clone or copy), but I have a few problems there too.

<Window x:Class="Hapyy_Copy_Clone_Day.View.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:viewModel ="clr-namespace:Hapyy_Copy_Clone_Day.ViewModel"
        xmlns:views ="clr-namespace:Hapyy_Copy_Clone_Day.View"
        xmlns:local="clr-namespace:Hapyy_Copy_Clone_Day.View"
        mc:Ignorable="d"
         Title="Copy-Clone" Height="500" Width="400"  d:DataContext="{d:DesignInstance viewModel:MainViewModel, IsDesignTimeCreatable=False}">
    <Window.DataContext>
        <viewModel:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>

        </Grid.ColumnDefinitions>
        <Grid>
            <ContentControl x:Name="mainContentControl"/>
        </Grid>
        <ItemsControl Grid.ColumnSpan="3" ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <views:CopyCloneUC/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
       

    </Grid>
</Window>

<UserControl x:Class="Hapyy_Copy_Clone_Day.View.CopyCloneUC"
             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:ccViewModel ="clr-namespace:Hapyy_Copy_Clone_Day.ViewModel"
             xmlns:local="clr-namespace:Hapyy_Copy_Clone_Day.View"
             mc:Ignorable="d" 
             d:DesignHeight="250" d:DesignWidth="400" d:DataContext="{d:DesignInstance ccViewModel:CopyCloneViewModel, IsDesignTimeCreatable=False}">
    
    <Grid Background="AliceBlue">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Column="0" Grid.ColumnSpan="2" >
            <TextBlock Height="50" Margin="10" Text="something wil be there" TextAlignment="Left" Padding="12">
                
            </TextBlock>
        </StackPanel>
        <Button Content="Copy" FontSize="30" FontFamily="bold" Grid.Row="1" Margin="20" Background="OrangeRed" Command ="{Binding Path= AddCopyCommand}" />
        <Button Content="Clone" FontSize="30" FontFamily="bold" Grid.Row="1" Grid.Column="1" Margin="20" Background="SlateBlue" Command ="{Binding Path= AddCloneCommand}" />


    </Grid>
</UserControl>
Posted
Updated 4-Oct-21 12:25pm
Comments
Chris Copeland 4-Oct-21 5:04am
   
Can you elaborate on "I have a few problems there too"? If you can explain what is and isn't working then we can provide suggestions.
Kamran Sultanlı 4-Oct-21 5:41am
   
Hi. First of all, in the AddCopy and AddClone Obects that you see in the MainViewModel. I need to write them what to do if this command comes. When the clone button is pressed, the view model I created must have a connection with the text box that I copied and copied again in the Main view model. so if I change any value, it should also change the text it copied. But I don't know how to get value from Text and send it back

There's two things you need to be aware of really. First is using binding to bind the content of the textboxes to a string within the CopyCloneViewModel. You would need to add a property to the view-model and then use binding in the XAML so that changes are applied. For example:
XML
<TextBox Text="{Binding Path=TextPropertyName, Mode=TwoWay}" />

This would mean that if you change the value in the textbox then it would copy it to the view-model, and also if the view-model changes then the textbox should see the update. You'd also need to make sure you do all of the appropriate binding contexts.

The second is simply how referencing objects work. For your clone, if you were to take the view-model that is being cloned and simply add the (exact same) object into your Items array then you would have two instances of the same object dynamically linked. And for your copy, if you were to create a brand new CopyCloneViewModel instance and add it to your array then you would have two different object references, not linked together, which would probably accomplish what you need.

Finally, consider using an ObservableCollection<> instead of the CopyCloneViewModel[] array as this is better for supporting collections which may have values added or removed.
   
Comments
Kamran Sultanlı 4-Oct-21 8:17am
   
Thank you for your recommendation. I put everything together and it's in worse shape now. I need to add the usercontrol to the window screen and it should do this every time when I press the button but it doesn't. even usercontrol does not come to the window screen at all. I have two viewmodles. one for main and the other for items, but I'm not quite sure what will be in main and what will be in copyclone. Since the commands will be in the usercontrol, I mainly thought to write it inside the copycolne, but I guess I made a big mistake. In addition to this, let's say we have a property called stringvalue binding to the textbox. How do I add the string value to the observableColletion.In short, I guess I'm not sure where the links are going.
Chris Copeland 4-Oct-21 8:24am
   
I think you need to do some research into WPF + data binding + observable collections. You would update your main view-model "Items" property to be an ObservableCollection<copycloneviewmodel>, this will add the automatic binding so when an item is added to the collection the visual tree gets refreshed and renders the new items.

You would add your new string property to the CopyCloneViewModel (since that is the object that is being rendered at that level of the tree), and bind the TextBox Text property to the new string property within your child control.

So think of it like this: your main view-model has the collection of CopyCloneViewModels, each of which has a text property. Then in your XAML you're creating a child control for each one of these view-models (correct), and then in your child control you'll bind your TextBox.Text property to the text property in the CopyCloneViewModel.
Kamran Sultanlı 4-Oct-21 9:52am
   
Okay. I tried to fix some issues but there should still be a few issues. There is no error around, but still the view in the usercontrol does not appear in the mainwindow.
Kamran Sultanlı 4-Oct-21 10:03am
   
public ObservableCollection<itemsviewmodel> Items { get; private set; }
public MainViewModel()
{
Items = new ObservableCollection<itemsviewmodel>();

}

This is how I made the observable part. this fixed a binding error but I still can't add the usercontrol to the main window

I am not sure what you are trying to achieve but it seems to me like you have an exercise in data binding. Here is an example that may be helpful.

<Window x:Class="ItemControls.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:ItemControls" xmlns:system="clr-namespace:System;assembly=System.Runtime"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="400">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <UniformGrid Grid.Row="1"  Margin="2,4,4,2"   HorizontalAlignment="Center" Rows="1" Columns="2" >
            <Button x:Name="AddItemButton"  Margin="5,2" Padding="5,5" 
                 Content="_Add Item"  Click="AddItemButton_Click" />
            <Button x:Name="CloneButton" Margin="5,2" Padding="5,5" 
                 Content="_Clone" Click="CloneButton_Click" />

        </UniformGrid>

        <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
            <ListBox Margin="10,10,0,10" Name="listBox1" HorizontalAlignment="Center" VerticalAlignment="Top" Width="190" Height="190"/>
        </ScrollViewer>
    </Grid>

</Window>

The main window has a list box and two buttons. Clicking the Add Item button adds a text box to the list. Clicking the clone button adds a cloned text box to the list. Cloned text boxes are bound to the same text property in the view model so updating one clone will change other clones to the same value. Here is the view model.
C#
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace ItemControls
{
    public class ItemsVM : INotifyPropertyChanged
    {
        private string textProperty;

        public ItemsVM()
        {
            TextProperty = "Cloned";

        }


        public string TextProperty
        {
            get { return textProperty; }
            set
            {
                textProperty = value;
                OnPropertyChanged();
            }
        }

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        protected virtual void OnAllPropertiesChanged()
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
        }
        #endregion
    }
}

The code behind handles the button clicks.
C#
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace ItemControls
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Binding cloneBinding;
        private int id = 1;
        public MainWindow()
        {
            InitializeComponent();
            ItemsVM itemsVM = new ItemsVM();
            DataContext = itemsVM;
            cloneBinding = new Binding("TextProperty");
            cloneBinding.Source = itemsVM;
        }

        private void AddItemButton_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add(new TextBox() { Text = $"Hello from number {id}" });
            id++;
        }

        private void CloneButton_Click(object sender, RoutedEventArgs e)
        {
            var clonedTextBox = new TextBox();
            clonedTextBox.SetBinding(TextBox.TextProperty, cloneBinding);
            listBox1.Items.Add(clonedTextBox);

        }
    }
}

   
Comments
Kamran Sultanlı 5-Oct-21 4:54am
   
Thank you so much @George Swan. Thank you for your effort. it will be help me. But this task has some conditions. the first is to never write anything in codebehind, the second is to use reactive ui. But I still think it will be of great help in some way to me.
George Swan 5-Oct-21 5:52am
   
You are very welcome. It was easy for me to do because I only had to adapt an existing test project. Personally, I am ok with using the code behind for matters relating to the UI, the view model should not be concerned with control elements, it should simply present items in a form that can easily be consumed by the view, but that is just my opinion.
Kamran Sultanlı 5-Oct-21 5:58am
   
The purpose of this homework is to learn mvvm designs so that I can write much cleaner codes. This is a bit difficult for me, my only hope is that one day I will understand and get used to it.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900