WPF: GroupBox.SetResourceReference suddenly makes it inherit TemplateParent.Foreground - wpf

I was making a UserControl when I found this strange phenomena. If I use C# code to place a GroupBox in the UserControl's template and then make any SetResourceReference call on the GroupBox, suddenly the GroupBox inherits the foreground of TemplateParent (my UserControl).
So far I have found the following requirements for this situation:
UserControl base type does not matter
Affected template child must be a GroupBox (but not necessarily the first template child)
The foreground of the GroupBox may be explicitly set in the template, overriding the inherit
Must be using some sort of reference call from the GroupBox
Only the Foreground property seems to be affected
Here is my sample code:
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="350">
<Window.Resources>
<Thickness x:Key="TestPadding">5</Thickness>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Foreground" Value="Red" />
<Setter Property="Background" Value="Orange" />
</Style>
</Window.Resources>
<Grid>
<my:TestControl Foreground="Blue" Background="Purple" />
</Grid>
</Window>
TestControl.cs:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Markup;
namespace WpfApplication1
{
public class TestControl : UserControl
{
public TestControl()
{
FrameworkElementFactory group = new FrameworkElementFactory(typeof(GroupBox));
group.SetValue(GroupBox.ContentProperty, "My Child");
group.SetResourceReference(GroupBox.MarginProperty, "TestPadding");
this.SetValue(TestControl.TemplateProperty, new ControlTemplate(typeof(TestControl)) { VisualTree = group });
}
}
}
What do you guys think, is this a bug that I should report to Microsoft?

I don't think that is a Microsoft issue. In fact i think it works fine. You are defining a Templeate in code behinde for your TestControl, and you are setting a GroupBox as root element for the template. What happens here is that your UserControl.Foreground property is not the same that the GroupBox that is the root of your template, then the GroupBox (as GroupBox) will take the Foreground that inherits from the Resources (in this case Window's Resources).
If you want to solve this, you can do something like "TemplateBindings", the follow code will works for you like a TemplateBinding:
namespace WpfApplication1
{
public class TestControl : UserControl
{
public TestControl()
{
FrameworkElementFactory group = new FrameworkElementFactory(typeof(GroupBox));
group.SetValue(GroupBox.ContentProperty, "My Child");
group.SetResourceReference(GroupBox.MarginProperty, "TestPadding");
//This line will work as a TeplateBinding
group.SetBinding(GroupBox.ForegroundProperty, new Binding() { Path = new PropertyPath("Foreground"), RelativeSource = RelativeSource.TemplatedParent });
this.SetValue(TestControl.TemplateProperty, new ControlTemplate(typeof(TestControl)) { VisualTree = group });
}
}
}
Hope this answer will be useful for you.

I have contacted the Microsoft WPF Development Team. They acknowledge this as a bug, but have prioritised it as low and not likely to be fixed.
My work around for this example:
Use another control to take the *.SetResourceReference call to perform padding, instead of the GroupBox.

Related

WPF MVVM Implementation

I have just started using WPF with MVVM pattern. I had gone through some material related to MVVM.
However, the project I have to work on has an implementation of MVVM that seems very different than what I have read (maybe incorrect as well, not sure).
The implementation has all the Views (controls or windows) implemented as ResourceDictionary where all the controls, in the view are in the "Style" element.
The code behind for such ResourceDictionary have all the DependencyProperty and the Commands (there is no other class for ViewModel). Also, the classes (code behind) some how inherit from the Windows.Controls.Control class.
Is this the correct implementation ? If not what are the reasons that you see that prove this as a wrong implementation.
I may be wrong but the reasons I see are the following:
Implementing views as ResourceDictionary is not correct and Resources are not for creating custom views.
Having minimal code in the code behind is one of the important aspects of MVVM, that allows for loosely coupled architecture.
Since all views inherit from Windows.Controls.Control, writing unit test cases for the views would be difficult.
Am I correct or there are some other reasons that this implementation is incorrect (or am I wrong and this can be a way to implement MVVM in WPF).
Your views are highly appreciated.
Below is a sample code: (XAML)
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Presentation"
>
<Style TargetType="{x:Type local:FirstControl}">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FirstControl}">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Height="490" DataContext="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneTime}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="TEST TEXT" FontWeight="DemiBold"/>
<Button Command="{Binding Path=CloseCommand, Mode=OneTime}"
Width="48" Height="30"/>
</StackPanel>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Code Behind:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace Presentation
{
/// <summary>
/// View-model
/// </summary>
public class FirstControl : Control
{
static FirstControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FirstControl), new FrameworkPropertyMetadata(typeof(FirstControl)));
}
public FirstControl()
{
CloseCommand = new DelegateCommand(OnCloseCommand);
}
private void OnCloseCommand()
{
// Write code to close application.
}
public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register("CloseCommand", typeof(ICommand), typeof(FirstControl));
public ICommand CloseCommand
{
get { return (ICommand)GetValue(CloseCommandProperty); }
set { SetValue(CloseCommandProperty, value); }
}
}
}
Hope this helps.
The DelegateCommand is a class to allow delegating command logic to methods passed as parameters.
The main point of MVVM is to allow each layer to be fully tested without the need of "higher" layers.
You should be able to test the Model, and in that test you should be able to successfully complete all the tasks required to send and retrieve data from your data store. Your model testing should not require any view or view-model to complete.
You should be able to test your View Model without the need for any UI code or other View level code. Your View Model should be able to logically do everything your application needs to do without any user interraction or UI code. Ideally, you should be able to test your ViewModel using mocked Model classes that provide predictable responses.

Custom control with constructor dependency not loading in VS designer

I can't figure this one out. I have a WPF application using the MVVM pattern with Unity constructor dependency injection. In the application, I use a custom control. All was well at first: I added the control to my main window and it displayed in the VS designer just fine. Then I wanted the control to do something useful, and to do so, it needed a data provider. I decided that the best way to provide that was to add the provider as a dependency in the constructor.
That's when everything went south. Although the program runs as expected, the VS designer can't instantiate the control. I've built a simple application to illustrate my dilemma.
MainWindow code behind:
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Unity;
namespace DependencyInjectionDesigner
{
public interface IDependency { }
class Dependency : IDependency { }
class DependentControl : Control
{
public DependentControl()
: this(App.Unity.Resolve<IDependency>()) { }
public DependentControl(IDependency dependency) { }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindow XAML:
<Window x:Class="DependencyInjectionDesigner.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DependencyInjectionDesigner"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type local:DependentControl}">
<Setter Property="Margin" Value="30"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DependentControl}">
<Border BorderBrush="Green" Background="Gainsboro"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<local:DependentControl/>
</Grid>
</Window>
App code behind:
using System.Windows;
using Microsoft.Practices.Unity;
namespace DependencyInjectionDesigner
{
public partial class App : Application
{
public static IUnityContainer Unity { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
if (Unity != null) return;
Unity = new UnityContainer();
Unity.RegisterType<IDependency, Dependency>(
new ContainerControlledLifetimeManager());
}
}
}
I think the problem is that the VS designer doesn't know to register the IDependency type before newing up the control. Am I correct? Is there way around this?
I'm using VS 2010 Ultimate and .Net 4.0.
The VS designer will try to call a zero-argument constructor using new to create the control in the designer; it knows nothing about and will not try to resolve through your container. In addition, your App.Unity property is not available to the designer, nor has setup code run.
Your best best to to change your control's constructor to use a stubbed out design time only data provider instead of trying to resolve through the container when using that constructor.

Custom control OnApplyTemplate called after dependency property callback

I'm developing my first WPF custom control and I'm facing some problems, here's a simplified version of the code I'm currently using:
using System.Windows;
using System.Windows.Controls;
namespace MyControls
{
[TemplatePart(Name = "PART_Button", Type = typeof (Button))]
public class MyControl : Control
{
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyControl), new PropertyMetadata(null, OnLabelPropertyChanged));
private Button _buttonElement;
public object Content
{
get { return this.GetValue(LabelProperty); }
set { this.SetValue(ContentProperty, value); }
}
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof (MyControl), new FrameworkPropertyMetadata(typeof (MyControl)));
}
private static void OnContentPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyControl myControl = sender as MyControl;
if (myControl != null && myControl._buttonElement != null)
myControl._buttonElement.Content = e.NewValue;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this._buttonElement = this.Template.FindName("PART_Button", this) as Button;
}
}
}
This is the template for my custom control:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyControls">
<Style TargetType="{x:Type local:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyControl}">
<Button x:Name="PART_Button" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Then i put it inside a Grid and try to set its Content property:
<Grid x:Name="layoutRoot">
<controls:MyControl x:Name="myControl" />
</Grid>
Here's the code behind:
using System.Windows;
namespace MyControls
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.myControl.Content = "test";
}
}
}
This doesn't work, for some reason the OnContentPropertyChanged callback is called before OnApplyTemplate, so myControl._buttonElement is assigned too late and it's still null when trying to set its content. Why is this happening and how can I change this behavior?
I also need to provide full design time support but I cannot find a way to make my custom control accept some additional markup, much like the Grid control does with ColumnDefinitions:
<Grid x:Name="layoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
Any help would be greatly appreciated!
UPDATE
I found a document that explains why the OnApplyTemplate method is called after control properies are set:
http://msdn.microsoft.com/en-us/library/dd351483%28v=vs.95%29.aspx
So the question is: how can I keep track of the properties that are set (in XAML or programmatically) and methods that are called when the control has not been initialized, so that I can set/call them when the OnApplyTemplate method is called? How can the same callback/method work both before and after the control initialization without duplicating the code?
UPDATE:
Instead of your "property" pushing changes in value into elements in your template by looking for Parts, you should instead have your template bind to properties on the control being templated.
Normally this is done using presenters within a template e.g. ContentPresenter binds to the property designated as the "content" (it finds out that name by looking for the [ContentProperty] attribute), and by using bindings in your template that use TemplateBinding or TemplatedParent to connect to the properties on your custom control.
Then there is no issue about what order you set your properties and when the template is applied....because it is the template that provides the "look" for the data/properties set on your control.
A custom control should only really need to know and interact with "parts" if it needs to provide certain behaviour/functionality e.g. hooking the click event on a button "part".
In this case instead of setting the Content in the constructor in code-behind, you should get your template to bind to the property. The example I gave below showed how that was generally done with a Content property.
Alternatively you could pull out properties more explicitly e.g. this could be inside your template.
<Label Content="{TemplateBinding MyPropertyOnMyControl}" .....
<Button Content="{TemplateBinding AnotherPropertyOnMyControl}" .....
I think it would be better to designate your "content" using [ContentProperty] attribute, and then using a ContentPresenter in your template so that it can be injected inside your Button, rather than you hooking your Content DependencyProperty. (if you inherit from ContentControl then that provides the "content" behaviour).
[TemplatePart(Name = "PART_Button", Type = typeof (Button))]
public class MyControl : Control
[ContentProperty("Content")]
and
<ControlTemplate TargetType="{x:Type local:MyControl}">
<Button x:Name="PART_Button">
<ContentPresenter/>
</Button>
</ControlTemplate>
As for you wanting to be able to specify some design time data via XAML like Grid does with ColumnDefinition....well that is just using Property Element syntax to specify the items to fill an IList/ICollection typed property.
So just create your own property that can hold a collection of the type you accept e.g.
public List<MyItem> MyItems { get; set; } // create in your constructor.
I have encountered similar problem, OnApplyTemplate was called before OnLoaded.
The problem was due to binding in xaml. I bound boolean property to Checked instead of IsChecked on one of my checkboxes.

WPF - How to Enable TextFormattingMode="Display" for all controls in the application?

I am currently programming WPF on Windows XP where anti-aliasing is rendered as blury text. We want to anti-aliasing on the whole WPF application by setting TextOptions.TextFormattingMode to Display. The code below solves the issue for all user controls and all its content, but not for windows we open from the application. What type should I set in TargetType to cover all Window and User Control elements in the application? Or are there better ways to do this?
<Style TargetType="{x:Type ContentControl}">
<Setter Property="TextOptions.TextFormattingMode" Value="Display"></Setter>
</Style>
That Style will only be applied to controls of type ContentControl, it will not be applied to types that derive from ContentControl (i.e. Button, Window, etc). That's just how implicit Styles work.
If you put that Style in your Application.Resources, then it would be applied to every ContentControl in your application, regardless of what Window the control is in. If you define that in the Resouces of a specific Window, then it would only be applied to ContentControls in that Window.
The TextOptions.TextFormattingMode property is inherited, which means you only need to set it at the top of the visual tree. So something like this should work, if placed in the Application.Resources:
<Style TargetType="{x:Type Window}">
<Setter Property="TextOptions.TextFormattingMode" Value="Display"></Setter>
</Style>
EDIT:
Or you could apply this to all Windows, even derived types, by overriding the default value like so:
using System.Windows;
using System.Windows.Media;
namespace MyProject
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application {
static App() {
TextOptions.TextFormattingModeProperty.OverrideMetadata(typeof(Window),
new FrameworkPropertyMetadata(TextFormattingMode.Display, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
}
}
}

WPF - Events on a ControlTemplate?

Does anyone know why I can't set an event on a control template??
For example, the following line of code will not compile. It does this with any events in a control template.
<ControlTemplate x:Key="DefaultTemplate" TargetType="ContentControl">
<StackPanel Loaded="StackPanel_Loaded">
</StackPanel>
</ControlTemplate>
I am using a MVVM design pattern and the control here is located in a ResourceDictionary that is added to the application's MergedDictionaries.
Does anyone know why I can't set an event on a control template??
Actually, you can... But where would you expect the event handler to be defined ? The ResourceDictionary has no code-behind, so there is no place to put the event handler code. You can, however, create a class for your resource dictionary, and associate it with the x:Class attribute :
<ResourceDictionary x:Class="MyNamespace.MyClass"
xmlns=...>
<ControlTemplate x:Key="DefaultTemplate" TargetType="ContentControl">
<StackPanel Loaded="StackPanel_Loaded">
</StackPanel>
</ControlTemplate>
C# code :
namespace MyNamespace
{
public partial class MyClass : ResourceDictionary
{
void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
...
}
}
}
(you might also need to change the build action of the resource dictionary to "Page", I don't remember exactly...)

Resources