Apply default Button style to custom Button class - wpf

I've created a custom IconButton class that inherits from Button and adds a few dependency properties to place an image in front of the button's text.
The code begins like this:
public partial class IconButton : Button
{
// Dependency properties and other methods
}
It comes with a XAML file that looks like this:
<Button x:Class="Unclassified.UI.IconButton" x:Name="_this" ...>
<Button.Template>
<ControlTemplate>
<Button
Padding="{TemplateBinding Padding}"
Style="{TemplateBinding Style}"
Focusable="{TemplateBinding Focusable}"
Command="{TemplateBinding Button.Command}">
<StackPanel ...>
<Image .../>
<ContentPresenter
Visibility="{Binding ContentVisibility, ElementName=_this}"
RecognizesAccessKey="True"
Content="{Binding Content, ElementName=_this}">
<ContentPresenter.Style>
...
</ContentPresenter.Style>
</ContentPresenter>
</StackPanel>
</Button>
</ControlTemplate>
</Button.Template>
</Button>
That works well so far. (But if you know a simpler way to override a Button's content without changing the entire template and placing a Button within the Button, please let me know. Every time I tried, Visual Studio 2010 SP1 immediately crashed the moment I closed the final XML tag.)
Now I've added some code to fix WPF's broken Aero2 theme for Windows 8. It's a separate ResourceDictionary that overwrites all sorts of default styles: (Based on this, via here)
<ResourceDictionary ...>
<Style TargetType="{x:Type Button}">
...
</Style>
</ResourceDictionary>
The new ResourceDictionary is added to the Application Resources on startup, in App.xaml.cs:
protected override void OnStartup(StartupEventArgs args)
{
base.OnStartup(e);
// Fix WPF's dumb Aero2 theme if we're on Windows 8 or newer
if (OSInfo.IsWindows8OrNewer)
{
Resources.MergedDictionaries.Add(new ResourceDictionary
{
Source = new Uri("/Resources/RealWindows8.xaml", UriKind.RelativeOrAbsolute)
});
}
...
}
This also works well for normal Button controls I place in my XAML views. (I'm still looking for a method to find out the real Windows theme instead of relying on the version number.)
But my IconButton control doesn't consider these new defaults and is still based on WPF's built-in Button style which is very basic. (It's really just a tight rectangle without all the details and interactivity that Win32 shows.)
I guess I need a way to tell my IconButton that it should re-evaluate the base style and see the newly added RealWindows8 styles. How can I do that?

I found the solution. There are two ways to accomplish this. Either one is sufficient.
The XAML way:
Add the Style attribute to the derived control. This presets the new control's style explicitly to whatever has been defined in the application as the Button style. StaticResource is sufficient for this. If a different Style is specified where the derived control is used, that will replace this initial value.
<Button Style="{StaticResource {x:Type Button}}" ...>
...
</Button>
The code(-behind) way:
Call the SetResourceReference method in the constructor of the derived class.
public IconButton()
{
// Use the same style as Button, also when it is overwritten by the application.
SetResourceReference(StyleProperty, typeof(Button));
...
}
I've tested this for my IconButton as well as a derived TabControl and TabItem class.
(Source)

Related

Set default text for textbox in WPF

I need all my textboxes to have a default text so I have done what is explained in this another post. I have used the solution proposed by Steve Greatrex and marked as accepted.
It is working for me but now I am trying to improve it to use in multiple textboxes as a template but for each of my textboxes I want to set a custom different default text.
As template default text is set to whatever, for example "Your Prompt Here" in above link, then all the textboxes I bind this template will have the same text.
I would like to put a different default text for each of my textboxes so how can I do it using the same controltemplate for all the textboxes?
Under "Windows.Resources" I have created an style that cotains the template indicated in the above post:
<Style x:Key="DefaultText" TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<!-- here the controltemplate from the above post -->
</Setter.Value>
</Setter>
</Style>
and I use it in my textboxes in the following way:
<TextBox Style="{StaticResource DefaultText}"/>
Use custom attached property instead of the Tag which has no any specific semantic:
public static class TextBoxHelper
{
public static readonly DependencyProperty DefaultTextProperty = DependencyProperty.RegisterAttached(
"DefaultText",
typeof(string),
typeof(TextBoxHelper));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static string GetDefaultText(FrameworkElement element)
{
return (string)element.GetValue(DefaultTextProperty);
}
public static void SetDefaultText(FrameworkElement element, string value)
{
element.SetValue(DefaultTextProperty, value);
}
}
Then you can use it from XAML:
xmlns:helpers="<your_namespace_with_helpers>"
<TextBox helpers:TextBoxHelper.DefaultText="..."/>
Then in your ControlTemplate you can set Text like this:
Text="{Binding Path=(helpers:TextBoxHelper.DefaultText), RelativeSource={RelativeSource TemplatedParent}}"
Although this approach is more verbose than using the Tag property I recommend you to use it because:
If your code will be maintained by someone else it will be quiet unexpected for this person that control relies on Tag property which can contain anything since its type is object.
DefaultText attached property has strict semantic. Anyone can say what it needed for just looking on its name and type.
Rule of thumb is always try to avoid using of properties with undefined semantic.
I have solved it by replacing Text property in textblock within control template by this one:
Text="{TemplateBinding Tag}"
then I call it from any textbox like below:
<TextBox Style="{StaticResource WatermarkedTextBox}"
Tag="Type whatever here" />
You can choose default text for each textbox by specifying the Tag property.
Also, this solution does not require the aero theme.
The solution that Clemens propose in this link also works and it is based on aero theme.

Can you programmatically set a property in a ControlTemplate from a ChildWindow?

I am working on a Silverlight application that has resource files that define styles for the different types of Child Windows in the application. The <Style> contains <ControlTemplate> markup with various content. Is there a way to set one of the properties of the controls defined within the <ControlTemplate> from the Child Window's class?
For example, imagine in the resource file I have markup like the following:
<Style x:Key="MyChildWindowStyle" TargetType="sdk:ChildWindow">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="sdk:ChildWindow">
<Grid x:Name="Root">
...
<Image Source="/Assets/image.png" />
...
</Grid>
</ContentTemplate>
</Setter>
</Style>
Now assume that I have a number of child windows that are configured to use this style. What I'd like to be able to do is from the code in those child windows be able to programmatically change the value of the image's Source.
Is this possible?
Thanks
In your resources you can do this:
<BitmapImage x:Key="MyImage" Source="/Assets/image.png"/>
<Style x:Key="MyChildWindowStyle" TargetType="sdk:ChildWindow">
...
<Image Source="{DynamicResource MyImage}" />
...
</Style>
Then in your child window's code-behind you can do this:
Resources["MyImage"] = new BitmapImage(new Uri("/Assets/other-image.png"));
But if your child window class is in another assembly you should be writing the uri a bit different:
Resources["MyImage"] = new BitmapImage(new Uri("pack://application:,,,/MyOtherAssemblyShortName;component/Assets/other-image.png"));
You can check msdn page for package uri format.
But I suggest you to use MVVM pattern in order to get most out of WPF in terms of bindings, styling etc. When you have a view model instead of a code-behind these things become simpler. You may wanna check the related msdn page, a codeproject sample, a toolkit or a validation mechanism designed for MVVM.
#zahir's answer pointed me in the right direction, but to get it to work in Silverlight I had to do the following:
First, I added the <BitmapImage> markup to my resource file, using the UriSource property to specify the default value.
<BitmapImage x:Key="MyImage" UriSource="../Assets/DefaultImage.png" />
Next, I referenced it in the <ControlTemplate> like so:
<Image ... Source="{StaticResource MyImage}"/>
Then, in my code-behind class I was able to modify the UriSource property like so:
BitmapImage img = (Application.Current.Resources["MyImage"] as BitmapImage);
if (img != null)
img.UriSource = "../Assets/NewImage.png";
Of course, the precise values for UriSource would depend on how you are handling image assets, where they are located, etc.

Split one big XAML in number of Sub-XAML files

In my WPF4 Desktop-based application there is a big block with sidebar menu that repeats in each window and takes about 70 lines of XAML. In order to improve code reuse, I would like to split XAML file in two files:
XAML-file that contains code for sidebar menu (≈70 lines)
Base XAML file that contains «include/reference» to XAML-file with sidebar menu code
As I understood, there are two ways to implement my problem:
Use ResourceDictionary
Use UserControl/CustomControl
My questions:
What is the difference between ResourceDictionary and UserControl? Could you give me examples where I have to use UserControl and where ResourceDictionary?
Could you give a full code example how to include/import content of one XAML-file to other?
P.S. Here is an example of code that I want to export to separate XAML-file:
<Border Style = "{StaticResource Body_SideBarMenu_Border_Settings}">
<StackPanel Style = "{StaticResource Body_SideBarMenu}">
<TextBlock Style = "{StaticResource Body_SideBarMenu_Title}"
Text = "{x:Static res:Resources.WinApp_SideBarMenu_Title}" />
<TextBlock x:Name = "SideBar_WinReports"
Style = "{StaticResource Body_SideBarMenu_Item}"
Text = "{x:Static res:Resources.DashListMarker}">
<Hyperlink KeyboardNavigation.TabIndex = "12"
Style = "{StaticResource Body_SideBarMenu_Item_Hyperlink}"
Click = "Call_WinReports_Click">
<TextBlock Text = "{x:Static res:Resources.WinApp_ModuleName_Reports}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</Border>
ResourceDictionary is just a container for your styles/templates etc. So you really have a choice between using a style (and referencing it through a ResourceDictionary) or a UserControl.
In order to differentiate between the two, ask yourself a question: are you implementing just another look for some existing control, or you are implementing something really new, which is more than just a ListView (or a Border, or a ComboBox etc.)? In the former case, use a style; in the latter, create a new UserControl.
Specifically for your case, I would go for a UserControl.
Code example (although not full)
(Please note that a template for the following code can be inserted with VS's "add new UserControl")
Xaml:
<UserControl x:Class="SomeNamespace.SidebarMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources> <!-- you can define local styles here -->
<Style x:Key="SidebarMenuTextblock" TargetType=TextBlock>
...
</Style>
</UserControl.Resources>
<Border Background=...>
<StackPanel>
<TextBlock
x:Name="Put_a_name_if_you_want_to_reference_this_item_in_code_behind"
Style="{StaticResource SidebarMenuTextblock}"
Text="{x:Static res:Resources.WinApp_SideBarMenu_Title}" />
...
</StackPanel>
</Border>
</UserControl>
.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace SomeNamespace
{
public partial class SidebarMenu : UserControl
{
public NumericUpDown()
{
InitializeComponent();
}
...
// define here your properties etc,
}
}
Now, you can use the control like that:
<Window
x:Class="SomeOtherNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:SomeNamespace">
<Grid>
<controls:SidebarMenu PropertyIfYouDefinedOne="SomeValue"/>
...
</Grid>
</Window>
If you can get your hands on Expression Studio, in Expression Blend, you can simply right click on any control and convert it to an user control. As easy as that.
User controls are good for splitting the XAML file. In essence, it is used to redefine the behavior of an existing control.
However, with User Controls, you can define whole WPF Layout Controls and convert them to an User Control, with the children content inside them. This is very helpful for a project spread across multiple developers, and can also be used to emulate the behavior of an MDI, which is kind of absent in WPF.

WPF UserControl inside another UserControl

I'm looking to set a UserControl to be the Content of another UserControl in XAML, in the same way you can set a Button's Content to be anything.
Let's say my "outer" UserControl looks like this:
<MyUserControl>
<Grid>
<Border FancyPantsStyling="True">
<-- I want to insert other controls here -->
</Border>
</Grid>
</MyUserControl>
And I'd like to instantiate this way:
<local:MyUserControl>
<local:MyUserControl.Content>
<local:AnotherControl />
</local:MyUserControl.Content>
</local:MyUserControl>
How do I design MyUserControl to render it's Content in a specific location?
All the stuff you put into your UserControl's XAML is its Content so you can't inject something else by setting the Content property. There are a few different ways you could handle this. If you don't have anything in the code-behind for MyUserControl you can just get rid of it and use something like:
<ContentControl>
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<Border FancyPantsStyling="True">
<ContentPresenter/>
</Border>
</Grid>
</ControlTemplate>
</ContentControl.Template>
<local:AnotherControl/>
</ContentControl>
If you have code behind that doesn't access the XAML elements directly you can do a similar thing with your existing control (since UC derives from ContentControl):
<local:MyUserControl>
<local:MyUserControl.Template>
<ControlTemplate TargetType="{x:Type local:MyUserControl}">
<Grid>
<Border FancyPantsStyling="True">
<ContentPresenter/>
</Border>
</Grid>
</ControlTemplate>
</local:MyUserControl.Template>
</local:MyUserControl>
If you need to keep the existing content connected to your code-behind you can use a DataTemplate to pass in the external content (into a new DP on MyUserControl) and apply that template to a ContentControl in the UC's XAML.
I got an idea, then tried it and it worked for me. I just wanted to share this to other people. I hope it will be useful.
The video link which explains what is the end of the solution: Video Link
The basic idea is to create UIElement DependencyProperty instead of creating Border DependencyProperty
Firstly, you should add your borders or panels or whatever you want to your user control (in your case it'S "MyUserControl") and make sure it has a name to access from .cs file:
<Border x:Name="LeftBorder" Grid.Column="0">
Then you should add a public UIElement value to your user control (in your case it's "MyUserControl"):
public UIElement LeftBorderChild
{
get { return (UIElement)GetValue(LeftBorderChildProperty ); }
set { SetValue(LeftBorderChildProperty , value); }
}
Secondly, type of your Dependencyproperty must be UIElement:
public static readonly DependencyProperty LeftBorderChildProperty = DependencyProperty.Register("LeftBorderChild", typeof(UIElement), typeof(MyUserControl), new PropertyMetadata(new PropertyChangedCallback(LeftBorderChildChanged)));
After these, typing events:
public static void LeftBorderChildChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyUserControl thisUserControl = d as MyUserControl;
thisCombobox._LeftBorderChildChanged(e); // Calling local event. The new child will be added in this local event function.
}
public void _LeftBorderChildChanged(DependencyPropertyChangedEventArgs e)
{
// In this function, new child element will be added to inside of LeftBorder
this.LeftBorder.Child = (UIElement)e.NewValue; // Sets left border child
}
We're done with this class. Let's call it from other class and add a control inside of it.
<local:MyUserControl Width="312" HorizontalAlignment="Right"
Margin="48, 0, 0, 0" VerticalAlignment="Center"
Height="56" >
<local:MyUserControl.LeftBorder>
<-- You can insert another control here -->
<-- Just don't remember that if you want to add more than one controls, you should add a panel then add controls into inside of the panel because Border child can only 1 child item -->
<StackPanel>
<-- Now you can insert your controls -->
</StackPanel>
</local:MyUserControl.LeftBorder>
</local:MyUserControl>
Note: When you do this firstly, you have to run your program before viewing in xaml designer. After running your program, all design systems are going to run synchronously.
I hope i understood what you mean and answered correctly.
Thank You
unless i misunderstood the question, you can use in your control and set its content to whatever you need.

WPF: How do I create a button "template" that applies a style & has an image in it?

There are multiple places in my WPF application where I need a button that looks & feels like a regular button, but:
It shows a specific icon on it (defined as {StaticResource EditIcon})
It applies a style (defined as {StaticResource GrayOutButtonStyle})
I would prefer to define these attributes in a single location, rather than repeating them each place the button is used. What is the appropriate way to do this in XAML?
--
If it helps, below is what I'm currently doing, but I was told this is wrong:
Updated: Is this the wrong way? Is there a way to fix this so that it is the "right way"?
I define the button as a template with the key EditButton:
<ControlTemplate x:Key="EditButton" TargetType="{x:Type Button}">
<Button Style="{StaticResource GrayOutButtonStyle}"
Command="{TemplateBinding Command}">
<Image x:Name="editImage" Source="{StaticResource EditIcon}" />
</Button>
</ControlTemplate>
Then, I declare a button with the template EditButton each place I want to use it in the application. I also indicate the Command to invoke here:
<Button Template="{StaticResource EditButton}" Command="..." />
Is this not right? What would be the correct way to do this?
A different approach:
Have you considered making a custom control? This way, you can create your own attributes to set the image contained in the button, and don't have to rely on multiple styles.
<myControl:MyButton x:Name="oneButton" ImageSource="MyButton.png" />
<myControl:MyButton x:Name="anotherButton" ImageSource="MyOtherButton.png" />
class MyButton {
private string imageSource;
public string ImageSource {
get {
return this.imageSource;
}
set {
this.imageSource = value;
//set the image control's source to this.imageSource
}
}
}
You can create a Style which targets all the Button of your app. Do do that, simply create a Style without giving it a Key:
<Style TargetType={x:Type Button}>
</Style>
Then in the Style, you can add a setter which sets the Template property:
<Setter Property="Template">
<Setter.Value>
<!-- whatever you want -->
</Setter.Value>
</Setter>

Resources