WPF: Creating Basic Core Management

Can I set a default style for a basic control in WPF?

Suppose I have the following base classes:

public abstract class View<T> : ContentControl where T : ViewModel { static View() { DefaultStyleKeyProperty.OverrideMetadata(typeof(View<T>), new FrameworkPropertyMetadata(typeof(View<T>))); } // Other properties, methods, etc in here } public abstract class ViewModel { // Other properties, methods, etc in here } 

Then suppose I have two classes that inherit from these base classes:

 public partial class TestView : View<TestViewModel> { public TestView() { InitializeComponent(); } // TestView specific methods, properties, etc } public class TestViewModel : ViewModel { /* TestViewModel specific methods, properties, etc */ } 

Now I want to specify a default style for a basic control that uses all of my derived controls:

 <Style TargetType="{x:Type local:View`1}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:View`1}"> <Border Background="Magenta" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <StackPanel> <Button>Test</Button> <ContentPresenter ContentSource="Content" /> </StackPanel> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> 

However, when I use my TestView control, I do not have the template layout applied (and therefore, any content that I can define in the XAML of my TestView control is not in the visual / logical tree).

I am basically trying to use the base view classes / viewmodel and apply a consistent look. This, of course, works in cases not related to a common basis. However, I need a secure connection type between view and viewmodel, so I can call methods on the viewmodel from everything that references the view (which, as I know, may not sit well with the way some people implemented MVVM) .

+8
wpf
source share
3 answers

I found a fairly simple solution using custom TypeExtension .

1. Set DefaultStyleKey to the standard generic type as indicated in the CodeNaked response :

  DefaultStyleKeyProperty.OverrideMetadata(typeof(View<T>), new FrameworkPropertyMetadata(typeof(View<>))); 

2 - Create the next class than inherited from System.Windows.Markup.TypeExtension

 [System.Windows.Markup.ContentProperty("Type")] public class TypeExtension : System.Windows.Markup.TypeExtension { public TypeExtension() : base() { } public TypeExtension(Type type) : base(type) { } public override object ProvideValue(IServiceProvider serviceProvider) { if (Type == null) throw new InvalidOperationException("Must specify the Type"); return Type; } } 

3 - Update the TargetType style to point to the new local:Type extension instead of the usual x:Type extension

 <Style> <Style.TargetType> <local:Type Type="{x:Type local:View`1}" /> </Style.TargetType> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Control}"> . . . 

Here it is .. There is a caveat, although VS throws a compilation error when you try to bind / set any dependency properties defined in the View <T> class. Therefore, you cannot use simple syntax, for example {TemplateBinding ViewTProperty} ...

+5
source share

Short answer: no

Long answer:

In your code behind you indicate DefaultStyleKey typeof(View<T>) , where T is the actual type allowed. In XAML, you efficiently execute typeof(Value<>) , where T is still "undefined".

You can set DefaultStyleKey to:

 DefaultStyleKeyProperty.OverrideMetadata(typeof(View<T>), new FrameworkPropertyMetadata(typeof(View<>))); 

This will find the style correctly, but will throw an exception (since TestView cannot be an argument to View<> ).

It's best to define your base style like you, but give it x: Key, for example, "ViewBaseStyle". Then create a style for each type of output that is based on ObjectOn ViewBaseStyle.

+1
source share

As I did this, I created a base class without generics and templates. Then I inherit the base class with a common class (non-template) that you can use to create your variations in the class (also non-template). In fact, everyone who inherits the base class (without generics) will have the same template.

For example,

 //You can define a template for this! public class ViewBase : UserControl { public ViewBase() : base() { DefaultStyleKey = typeof(ViewBase); } } //Use for class variations public class View<T> : ViewBase { public View() : base() { //Do whatever } } //Example class variation public class DecimalView : View<decimal> { public DecimalView() : base() { //Do whatever } } 

ViewBase , View<T> and DecimalView now all have the same default style. In addition, you can also specify an individual style for each class change based on the original style ( ViewBase ), and not just for the general class.

It is worth noting that the best way to bind to the properties of a top-level class is to use the syntax {Binding Path, RelativeSource={RelativeSource AncestorType={x:Type ViewBase}}} compared to {TemplateBinding Path} . The latter, as well as {Binding Path, RelativeSource={RelativeSource TemplatedParent}} , applies only to properties owned by ViewBase .

0
source share

All Articles