Search code examples
c#wpfmvvmtabitem

Copying a TabItem with an MVVM structure


This is an attempt to expand on this question. In my WPF program I've been cloning tabItems by using an XamlWriter in a function called TrycloneElement. I originally found this function here, but the function can also be viewed in the link to my previous question.

Now that I am beginning to worry about functionality inside my program, I found that the TrycloneElement function does not replicate any code-behind functionality assigned to the tabItem that it is cloning.

Because of High Core's link and comment on my earlier question I decided to start implementing functionality on my tabItems through Data Binding with my ViewModel.

Here is a sample of a command that I've implemented:

public viewModel()
{
    allowReversing = new Command(allowReversing_Operations);
}

public Command AllowReversing
{
       get { return allowReversing; }
} 

private Command allowReversing; 

private void allowReversing_Operations()
{
       //Query for Window1
       var mainWindow = Application.Current.Windows
           .Cast<Window1>()
           .FirstOrDefault(window => window is Window1) as Window1;

       if (mainWindow.checkBox1.IsChecked == true) //Checked
       {
           mainWindow.checkBox9.IsEnabled = true;
           mainWindow.groupBox7.IsEnabled = true;
       }
       else //UnChecked
       {
           mainWindow.checkBox9.IsEnabled = false;
           mainWindow.checkBox9.IsChecked = false;
           mainWindow.groupBox7.IsEnabled = false;
       }
} 

*NOTE: I know that I cheated and interacted directly with my View in the above code, but I wasn't sure how else to run those commands. If it is a problem, or there is another way, please show me how I can run those same commands without interacting with the View like I did.

Now to the question:

After changing my code and adding the commands to my ViewModel, the TrycloneElement function no longer works. At run time during the tab clone I receive an XamlParseException on line, object x = XamlReader.Load(xmlReader); that reads: enter image description here

I'm fine with ditching the function if there is a better way and I don't need it anymore. But ultimately, how do I take a tabItem's design and functionality and clone it? (Please keep in mind that I really am trying to correct my structure)

Thank you for your help.

Revision of Leo's answer

This is the current version of Leo's answer that I have compiling. (There were some syntax errors)

public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
       return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues) })
                    select DependencyPropertyDescriptor.FromProperty(pd)
                   into dpd
                   where dpd != null
                   select dpd.DependencyProperty).ToList();
}

public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
                                                   FrameworkElement controlToCopy)
{
       foreach (var dependencyValue in GetAllProperties(controlToCopy)
                    .Where((item) => !item.ReadOnly)
                    .ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
       {
           controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
       }
}

Solution

  • Here is my example of a properly-implemented dynamic TabControl in WPF.

    The main idea is that each Tab Item is a separate widget that contains its own logic and data, which is handled by the ViewModel, while the UI does what the UI must do: show data, not contain data.

    The bottom line is that all data and functionality is managed at the ViewModel / Model levels, and since the TabControl is bound to an ObservableCollection, you simply add another element to that Collection whenever you need to add a new Tab.

    This removes the need for "cloning" the UI or do any other weird manipulations with it.