I'm working with WPF and I want to use attached properties to work with some styling things in the validation of the controls (my example of the problem is really simple, binding a simple text).
This is my attached property:
public class ToolTipExtension
{
public static readonly DependencyProperty ShowToolTipProperty = DependencyProperty.RegisterAttached(
"ShowToolTip", typeof(string), typeof(ToolTipExtension), new PropertyMetadata("Deffault"));
public static void SetShowToolTip(DependencyObject element, string value)
{
element.SetValue(ShowToolTipProperty, value);
}
public static string GetShowToolTip(DependencyObject element)
{
return (string) element.GetValue(ShowToolTipProperty);
}
}
I have a simple style dictionary like this
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:at="clr-namespace:CarpetaTecnicaWPF.AttachedProperties"
>
<Style TargetType="{x:Type TextBox}" x:Key="Blah" >
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(at:ToolTipExtension.ShowToolTip)}" FontSize="50"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
As you can see, I'm trying to bind the Text property to my attached property.
In my Page, I'm using the style like this:
<TextBox Style="{StaticResource Blah}" at:ToolTipExtension.ShowToolTip="Prueba?"/>
The thing is, the value Prueba? does not appear. When I inspect the tree, I see this:
But in runtime, the result of the binding is Deffault
What am I doing wrong?
Your binding is incorrect.
The TemplatedParent in this case is not what you actually need. The ControlTemplate for the error is not applied to the text box itself, it's a stand-alone control template. So you are just getting a default value from a wrong FrameworkElement.
To access the text box your error template is applied to, you need to use the AdornedElementPlaceholder in your ControlTemplate. From that AdornedElementPlaceholder, you can access your text box via the AdornedElement property.
Here is an example:
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<AdornedElementPlaceholder x:Name="adorner"/>
<TextBlock Text="{Binding ElementName=adorner, Path=AdornedElement.(at:ToolTipExtension.ShowToolTip)}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Related
I have a class assembly that contains a number of user controls. I want to add a new type of button to this class assembly and I also want to add styling to it. Specifically I want a button that contains multiple images for use in Normal, Hover (IsMouseOver) and disabled states. It will also contain text to display. Then I could add this in any application using this class assembly as follows:
<ns:ImageTextButton NormalImage="{StaticResource SomeImage}" HoverImage="{StaticResource SomeHoverImage}" Text={StaticResource SomeText}" />
First I created a C# class:
public class ImageTextButton : Button {
public DependencyProperty ImageProperty = DependencyProperty.Register(nameof(Image), typeof(DrawingBrush), typeof(ImageTextButton));
public DependencyProperty HoverImageProperty = DependencyProperty.Register(nameof(HoverImage), typeof(DrawingBrush), typeof(ImageTextButton));
public DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(ImageTextButton));
public DrawingBrush Image {
get { return GetValue(ImageProperty) as DrawingBrush; }
set { SetValue(ImageProperty, value); }
}
public DrawingBrush HoverImage {
get { return GetValue(HoverImageProperty) as DrawingBrush; }
set { SetValue(HoverImageProperty, value); }
}
public string Text {
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value); }
}
}
Then I created a style in a Styles.xaml file which is compiled as a Resource.
<Style TargetType="{x:Type local:ImageTextButton}" x:Key="ImageTextButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="0" Background="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Image}">
<StackPanel>
<Canvas x:Name="canvas" Background="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Image}" />
<TextBlock Style="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=TextStyle}"
Text="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Text}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=HoverImage}" TargetName="canvas" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have not been able to test this style and there very well may be issues in it. I can't get the application to start because the TargetType above doesn't exist. I believe this is because my Styles.xaml is a resource and the actual class is compiled. The error I get is Failed to create a 'Type' from the text 'local:ImageTextButton'.
How can I achieve this? Secondly, is there any way to apply this style by default to this type? I don't want to always have to specify Style={StaticResource ImageTextButtonStyle} in each instance of this user item.
First you have to decide if you are going to build an UserControl or a CustomControl.
UserControl needs to be derived from "UserControl" Base class. As your C# code extends "Button", that implementation doesn't fall under "UserControl".
For a CustomControl, your Style should be present in a file named "Generic.xaml" which SHOULD BE PLACED in a folder "Themes". (You can still change the default Themes Folder location). And your C# file should define the Key which will be used to find/target/identify the Xaml Style.
Your above setup should not work as it satisfies neither Usercontrol nor Custom Control Requirements.
I cannot create a full example but can direct you to a certain extent.
You need to have the below Static Method to set your default style as a bare minimum. Add remaining properties as required.
public class ImageButton : Button
{
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public ImageButton()
{
}
For the Xaml Part. Create a Folder Themes and inside that create a resource dictionary by name "Generic.xaml". Inside your Generic.Xaml, add your styles. (Place your xaml logic inside the control template).
<Style TargetType="{x:Type bc:ImageButton}">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type bc:ImageButton}">
</ControlTemplate>
</Setter>
</Style>
All the above are bare minimum requirement for a CUSTOM CONTROL. In case you wish to create a UserControl, you need a .xaml & a .xaml.cs files. You can easily use visual studio context menu (rightclick on solution) and create a usercontrol.
Important: When you are creating a custom control, you dont need something like below
Background="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Image}">
Just use, TemplateBinding
Background="{TemplateBinding Image}">
The RelativeSource Mode FindAncestor is missing
change {RelativeSource AncestorType=local:ImageTextButton} to {RelativeSource FindAncestor, AncestorType=local:ImageTextButton}
I have created a Style for a Button with an Image:
<Style x:Key="StyleButtonBase" TargetType="Button">
<Style.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Sizes/Sizes.xaml" />
<ResourceDictionary Source="../Colors/Brushes.xaml" />
<ResourceDictionary Source="../Fonts/Fonts.xaml" />
<ResourceDictionary Source="../Images/Images.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource BrushButtonActive}" />
<Setter Property="Foreground" Value="{StaticResource BrushForegroundLight}" />
<Setter Property="FontFamily" Value="{StaticResource FontFamilyDefault}" />
<Setter Property="FontSize" Value="{StaticResource DoubleFontSizeStandard}" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{StaticResource BrushBorder}"
BorderThickness="{StaticResource ThicknessBorder}"
CornerRadius="{StaticResource CornerRadius}">
<Image Source="{StaticResource IconIcon}" Stretch="None" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource BrushButtonPressed}" />
</Trigger>
</Style.Triggers>
</Style>
Now I Want to create a User-Control which only consists of a Button with this style and a Dependency Property to set the Button Image. The XAML part of my user control looks like this:
<UserControl
x:Class="HH.HMI.ToolSuite.ResourceLib.Controls.ButtonSmall">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Styles/Buttons.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Button Style="{StaticResource StyleButtonBase}" Width="{StaticResource DoubleWidthButtonSmall}" Height="{StaticResource DoubleHeightControls}">
</Button></UserControl>
The code behind of my user-control looks like this:
public partial class ButtonSmall : UserControl, INotifyPropertyChanged
{
public ButtonSmall()
{
InitializeComponent();
}
public static readonly DependencyProperty ButtonImageProperty
= DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(TextOutput), new PropertyMetadata(null, OnButtonImagePropertyChanged));
private static void OnButtonImagePropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
ButtonSmall temp = dependencyObject as ButtonSmall;
temp.OnPropertyChanged("ButtonImage");
temp.OnButtonImagePropertyChanged(e);
}
private void OnButtonImagePropertyChanged(DependencyPropertyChangedEventArgs e)
{
ButtonSmallImage.Source = ButtonImageSource;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ImageSource ButtonImageSource
{
get { return (ImageSource)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
}
In my other user-controls i usually access an element in the user control itself like:
xamlname.text = text
Now i haven't a named element in my xaml code of the user-control. Instead i have the named element in the style, which i reference in the user control. How can access this throug my code behind?
If I were you I'd subclass Button and create a new class (just a .cs file) like so:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace MyProject
{
public class IconButton : Button
{
public static readonly DependencyProperty ButtonImageProperty = DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(IconButton),
new FrameworkPropertyMetadata(new BitmapImage(), FrameworkPropertyMetadataOptions.AffectsRender));
public ImageSource ButtonImage
{
get { return (ImageSource)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
}
}
This means you can now reference the property. Otherwise, because your button is just a regular button (with only a regular button's properties; no image), your style doesn't know about your new image property which it expects a Button to have. Don't forget to update your style's TargetType's to point to IconButton.
If you place your style in the resources section of your User Control, you can set the button style like so:
<UserControl x:Class="MyProject.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myclass="clr-namespace:MyProject">
<UserControl.Resources>
<!-- your style here -->
</UserControl.Resources>
<myclass:IconButton Style="{StaticResource StyleButtonBase}/>
</UserControl>
(The xmlns 'myclass' must be replaced to refer to the namespace your custom button is in!)
Also, if you remove the x:Key property from the style, it will apply to all buttons in scope, meaning you can omit setting it explicitly. This may be handy if you locate it in a shared ResourceDictionary (if you're building a library of custom controls for example) (if you do this, you will need to combine this resource dictionary in your App.xaml.cs file). If you end up doing that and you discover your UserControl doesn't have any especial functionality beyond wrapping an IconButton, you can of course omit it entirely and just use IconButtons directly in other controls. Your style declares how your IconButton looks, and your IconButton class ensures that the resources (your image) your style expects are there when it looks for them at runtime, so as long as your style is in scope, you're good to go.
If the Style is defined in Application.Resources in App.xaml, or in a resource dictionary that's merged into Application.Resources in App.xaml, you can just reference it in the user control via StaticResource. If it's in another resource dictionary, you'll have to merge that one into UserControl.Resources.
Or you can put it directly in UserControl.Resources as TernaryTopiary suggests, if it won't be needed elsewhere.
As for the image source property, you could write a Button subclass as Ternary suggests, or you could write an attached property (see below). In XAML, when you customize controls, first you try to do it with regular attributes; then you try to restyle the thing. Then you escalate to replacing the control template, and that won't quite do the job, you consider attached properties/behaviors. Only if all else fails to do you resort to subclassing. You should know how to do it, but you should also learn the other ways of doing things.
In this case, there's a somewhat quick and dirty way to do it that's consistent with the correct XAML way of doing things: The Content property of the button is going unused, and its declared type is Object, so we can just use that. Since we're using a binding to pass in the image source, you can get rid of that PropertyChanged handler on ButtonImageSource.
<UserControl
...>
<!-- ... -->
<Button
Content="{Binding ButtonImageSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
Style="{StaticResource StyleButtonBase}"
Width="{StaticResource DoubleWidthButtonSmall}"
Height="{StaticResource DoubleHeightControls}"
/>
And make the following change in the control template in StyleButtonBase:
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{StaticResource BrushBorder}"
BorderThickness="{StaticResource ThicknessBorder}"
CornerRadius="{StaticResource CornerRadius}"
>
<!-- Now we'll find the image source in the button's Content --->
<Image
Source="{TemplateBinding Content}"
Stretch="None"
/>
</Border>
</ControlTemplate>
Attached Property
Using Content for this is a very mild abuse of WPF: You shouldn't really be repurposing properties. Content is universally understood in WPF to mean "any arbitrary content", not "any ImageSource".
So it's a little more "correct" to use an attached property, and it's not a lot more work. Here's how that would look.
We'll define the attached property in a separate static class, because I can't think of a good name for it other than ButtonImageSource, which you're already using in SmallButton:
public static class ButtonHelper
{
#region ButtonHelper.ButtonImageSource Attached Property
public static ImageSource GetButtonImageSource(Button obj)
{
return (ImageSource)obj.GetValue(ButtonImageSourceProperty);
}
public static void SetButtonImageSource(Button obj, ImageSource value)
{
obj.SetValue(ButtonImageSourceProperty, value);
}
public static readonly DependencyProperty ButtonImageSourceProperty =
DependencyProperty.RegisterAttached("ButtonImageSource", typeof(ImageSource), typeof(ButtonHelper),
new PropertyMetadata(null));
#endregion ButtonHelper.ButtonImageSource Attached Property
}
In the XAML, the user control uses this attached property instead of Content:
<Button
Style="{StaticResource StyleButtonBase}"
local:ButtonHelper.ButtonImageSource="{Binding ButtonImageSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
And the control template does likewise:
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
>
<Image
Source="{TemplateBinding local:ButtonHelper.ButtonImageSource}"
Stretch="None"
/>
</Border>
</ControlTemplate>
All the attached property does is give us a property that isn't named Content, and which is strongly typed as ImageSource, which we can use to pass in that image source.
Another thing: Maybe this was an error that crept in when you simplified your code for the question, but you're passing typeof(TextOutput) to DependencyProperty.Register() where you should be passing typeof(ButtonSmall). More importantly, you've got two names for what should be a single property: ButtonImage and ButtonImageSource.
public static readonly DependencyProperty ButtonImageSourceProperty
= DependencyProperty.Register(
"ButtonImageSource",
typeof(ImageSource),
// Should be ButtonSmall, not TextOutput
typeof(ButtonSmall),
new PropertyMetadata(null));
public ImageSource ButtonImageSource
{
get { return (ImageSource)GetValue(ButtonImageSourceProperty); }
set { SetValue(ButtonImageSourceProperty, value); }
}
Incidentally, in your Style, it would be better practice to use TemplateBinding for BorderBrush and BorderThickness, and set the defaults in Style setters, the way you did with Background.
In a custom control I'd like to use a Style (given as DependencyProperty) for a TextBlock in my Template.
MyControl.cs
public static DependencyProperty HeadingStyleProperty =
DependencyProperty.Register("HeadingStyle",
typeof (Style),
typeof (MyControlElement),
new PropertyMetadata(new Style(typeof(TextBlock))));
public Style HeadingStyle {
get { return (Style) GetValue(HeadingStyleProperty); }
set { SetValue(HeadingStyleProperty, value); }
}
MyControl.xaml
<ResourceDictionary ...>
<Style TargetType="local:MyControl">
<Style.Resources>
<!-- Getting error on BasedOn="TemplateBinding -->
<Style TargetType="TextBlock" BasedOn="{TemplateBinding HeadingStyle}" x:Key="Heading" />
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Style="{StaticResource Heading}" Text="StyledHeading" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I get compiler error 'HeadingStyle' member is not valid because it does not have a qualifying type name.
Do I have to modify the DataType of my DP from Style to a more specific one? Or what causes this error. The initial value of DP is set to Style for TargetType TextBlock...
First of all, Style does not derive from DependencyObject, so you cannot set any binding on it.
If you want your TextBlock (which is part of the template) to be styled by your property, simply set the TemplateBinding on its Style property directly (what would be the purpose of the style you defined as a resource anyway?). This can be done in two ways. One is to use fully qualified property name:
<ControlTemplate>
<TextBlock Style="{TemplateBinding local:MyControl.HeadingStyle}" (...) />
</ControlTemplate>
The other way, more commonly used, is to use simplified property name, but it requires the ControlTemplate.TargetType to be specified:
<ControlTemplate TargetType="{x:Type local:MyControl}">
<TextBlock Style="{TemplateBinding HeadingStyle}" (...) />
</ControlTemplate>
The second method works because the XAML parser is smart enough to know how to resolve the HeadingStyle property - if you didn't specify ControlTemplate.TargetType, you'd get the exact same error you're getting right now.
You could of course trick the compiler to compile your code by using fully qualified property name:
<Style x:Key="Heading" BasedOn="{TemplateBinding local:MyControl.HeadingStyle}" (...) />
but that would result in a runtime error saying that TemplateBindingExpression cannot be converted to Style.
I have a control that is wrapping an Xceed DataGridControl (part of the Extended WPF Toolkit Community Edition). The control provides a simple property (without a backing dependency property) that can hold a list of buttons (field instantiated by the constructor):
public List<Button> GroupButtons
{
get { return groupButtons; }
set { groupButtons = value; }
}
The items of the property are then added in the XAML of a view that is using the control:
<local:CustomControl ...>
<local:CustomControl.GroupButtons>
<Button>foo<Button>
</local:CustomControl.GroupButtons>
...
</local:CustomControl ...>
I would like to render the buttons of this list inside the so-called "GroupHeaderControl" of the Xceed Datagrid, which is basically a grouping row like shown below:
To achieve this, I've overwritten the ControlTemplate of the GroupHeaderControl:
<ResourceDictionary ...>
<Style TargetType="{x:Type controls:CustomControl}">
<Style.Resources>
<Style TargetType="{x:Type xcdg:GroupHeaderControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type xcdg:GroupHeaderControl}">
<Border ...>
<StackPanel Height="{TemplateBinding Height}" Orientation="Horizontal">
<ContentPresenter />
<ItemsControl ItemsSource="{Binding GroupButtons, RelativeSource={RelativeSource AncestorType={x:Type controls:CustomControl}}}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
</Style>
...
</ResourceDictionary>
Now here comes the problem: Instead of rendering the button(s) for each instance of the GroupHeaderControl, it is rendered only once. For illustration, imagine that in the image above only the button at the second group header ("Lyon") is visible while the other one ("Reims") is not.
The problem is apparently related to the fact that the items of the GroupButtons list are added via the XAML definition. If I hard code the items of the list, it works like a charm:
public List<Button> ButtonList
{
get { return new List<Button>()
{
new Button() { Content = "foo" }
}
}
I don't really get where this behavior is coming from. Does somebody have an idea?
I've been stuck on trying to reuse layout code for my WPF application.
I'm trying to make an XML editor that lets you have multiple files open (via tabs).
My situation is as follows:
<TabControl>
<TabItem>
// Layout XAML with various {Binding} sources (File 1)
</TabItem>
<TabItem>
// Layout XAML with various {Binding} sources (File 2)
</TabItem>
<TabItem>
// Layout XAML with various {Binding} sources (File 3)
</TabItem>
</TabControl>
This works; however, each of the three TabItems is a huge chunk of copy & pasted code, with only a few names changed to avoid duplicate names.
I want to rewrite the code in such a way that something like this is possible:
<TabControl>
<TabItem>
// Reference to Template
</TabItem>
<TabItem>
// Reference to Template
</TabItem>
<TabItem>
// Reference to Template
</TabItem>
</TabControl>
And have a Template defined somewhere else.
I tried using a DataTemplate for the template, and assigning it to each TabItem with ContentTemplate, but although the layout displayed properly, all of the {Bindings} were lost.
I've googled extensively, but haven't been able to figure out how I should be approaching this.
I would greatly appreciate any links to demos that would show how to achieve binding without copy & pasting code.
I would also appreciate any tips for debugging failed bindings, other than trying things out until they work. (I'm comfortable debugging C# with the debugger, but not sure how to inspect XAML stuff)
Thanks in advance!
You should represent your tab items with an ObservableCollection, using the Window's ViewModel.
<TabControl ItemsSource="{Binding Path=TabItems, Mode=OneTime}" SelectedValue="{Binding Path=SelectedTab, Mode=TwoWay}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border" Margin="0,0,-4,0" BorderThickness="1">
<ContentPresenter
x:Name="ContentSite"
HorizontalAlignment="Center"
Margin="12,2,12,2"
VerticalAlignment="Center"
ContentSource="Header"
RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
etc...
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
etc...
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
Each tab item would then be a view model itself, with all the data inside that you need to bind to for each tab. So for example:
public ObservableCollection<TabItemViewModel> TabItems
{
get
{
return m_SuspendTabItems;
}
private set
{
if (Equals(m_SuspendTabItems, value))
{
return;
}
m_SuspendTabItems = value;
NotifyPropertyChanged(s_SuspendTabItems);
}
}
Would be on your main WindowViewModel. To add a new tab, you would simply call TabItems.Add(new TabItemViewmodel());.
Where "TabItemViewModel" contains your binding for that particular tab item.
I would suggest to write a custom UserControl containing everything you now are copy-pasting, and add this UserControl into the tab items. Add to this UserControl your required sources as a Dependency Property. Now you can access it from your TabControl without loosing Bindings.
I made a quick non-working example:
MyControl.xaml
<UserControl Name=this>
<StackPanel>
<TextBox Text={Binding Something, ElementName=this} />
</StackPanel>
</UserControl>
MyControl.xaml.cs
public partial class MyControl : UserControl
{
public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register("Something", typeof(string), typeof(MyControl));
public string KeyType
{
get { return (string)GetValue(SomethingProperty ); }
set { SetValue(SomethingProperty , value); }
}
}
program.xaml
<Window>
<TabControl>
<TabItem>
<MyControl Something={Binding Anything[0] />
</TabItem>
<TabItem>
<MyControl Something={Binding Anything[1] />
</TabItem>
//...
</TabControl>
</Window>
program.xaml.cs
//...
public string[] Anything { get; set; }
//...
Be aware that this is only a very simple example. You could easily add your required model as mentioned above to an ObservableCollection and generate the tab items from that automatically.