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);
       }
}
有帮助吗?

解决方案

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.

其他提示

1.) To fix that XamlParseException, make sure you have a public constructor like an empty one, you probably defined a constructor and when you tried to serialize that object and deserialize it can't. You have to explicitly add the default constructor.

2.) I don't like the word clone, but I'd say, when they want to copy. I'll manually create a new tab item control then do reflection on it.

I have this code that I made

  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);
        }
    }

So it would be like

var newTabItem = new TabItem();
newTabItem.CopyPropertiesFrom(masterTab);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top