Add a control using XAML but instantiated using code-behind - wpf

I'm trying to understand how XAML and code-behind talk to each other. I know that code-behind can access an element instantiated in XAML using the Name attribute eg:
Instantiate the button in XAML:
<SomeControlParent controlParent>
<Button Name=button1/>
<SomeControlParent controlParent>
Change properties of the button in code-behind:
button1.Content = "I created this button in XAML"
I was wondering if it was possible to do the opposite using XAML eg:
Instantiate the button in code-behind:
Button button1 = new Button();
controlParent.Child.Add(button1);
and then change the Content of the button using XAML.
Thanks!
Soumaya

Having a code-behind lets you reference elements that have x:Name defined on them in XAML. Going the other direction, you can define properties on your UserControl and then reference them in XAML using a RelativeSource binding:
{Binding MyProperty, RelativeSource={RelativeSource Self}}
So in your example, you could have a property on your UserControl (although you'd probably want it to be a dependency property so you have change notification):
public Button Button1 { get; private set; }
And then insert it into your XAML using:
<ContentControl Content={Binding Button1, RelativeSource={RelativeSource Self}}>
<ContentControl.Resources>
<Style TargetType="Button">
<Setter Property="Content" Value="Hey, I changed the name in XAML!"/>
</Style>
</ContentControl.Resources>
</ContentControl>

Related

WPF Set Visibility on DataTemplate UI Element from MVVM View Model

I have a control that is set up as a DataTemplate:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<DataTemplate x:Key="KEYBOARD_EN">
<StackPanel>
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
</StackPanel>
</DataTemplate>
In this DataTemplate there is a control on which I wish to set the Visibility from various view models:
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource ...} > Register </Button>
I do routed events with my control, so I tried to set up something similar, but no matter what I try, the RegisterButtonVisible property does not get picked up:
public partial class MainKeyboard : UserControl
{
public static DependencyProperty RegisterButtonVisibleProperty;
public Visibility RegisterButtonVisible
{
get { return (Visibility)GetValue(RegisterButtonVisibleProperty); }
set { SetValue(RegisterButtonVisibleProperty, value); }
}
static MainKeyboard()
{
RegisterButtonVisibleProperty = DependencyProperty.Register("RegisterButtonVisible", typeof (Visibility),
typeof (MainKeyboard));
}
}
In my ViewModel I do this:
public Visibility RegisterButtonVisible // get, set, raisepropchange, etc
My DataTemplate with the button in it is wrapped in a userControl:
<UserControl x:Class="Bleh.Assets.MainKeyboard"
x:Name="TheControl"
Unloaded="UserControl_Unloaded">
<Viewbox>
<Grid>
<ContentControl Name="ctrlContent" Button.Click="Grid_Click" />
</Grid>
</Viewbox>
and is used in my views like this:
<assets:MainKeyboard
RegisterButtonVisible="Collapsed"
Loaded="MainKeyboard_Loaded">
<b:Interaction.Triggers>
<b:EventTrigger EventName="Register">
<b:InvokeCommandAction Command="{Binding ConfirmEmailAddressCommand}"/>
</b:EventTrigger>
<b:EventTrigger EventName="Enter">
<b:InvokeCommandAction Command="{Binding EnterKeyCommand}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</assets:MainKeyboard>
Please note this attribute:
RegisterButtonVisible="Collapsed"
This is my dependency property. It shows up in intelliesense, so the CLR has registered it correctly, but it does NOT pick up the property assignment (Collapsed is ignored).
This makes me feel like it is very close, but I do remember someone telling me I can not do this, thus the EventTriggers (this is a common issue with datatemplates and MVVM apparently).
So one option is to use something in the Interaction namespace, like I do my event triggers ( I just need to fire a "Visibility" trigger on this button somehow, at least I figure).
What is the right ANY way to do this in MVVM?
Fixing your code
In order to make your existing code work, you need to tell need to tell WPF what object RegisterButtonVisible should be read from. If it's a user control, give the UserControl a name and then reference that element via ElementName, like so:
<UserControl ... lots of stuff here
x:Name="TheControl"
>
In your button binding:
<Button Visibility="{Binding ElementName=TheControl, Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, if you can't do that because the button and the usercontrol are in different files, you can still use an ancestor binding:
<Button Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type assets:MainKeyboard}},
Path=RegisterButtonVisible}"
Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
which, for each button, will walk up to find the closest instance of assets:MainKeyboard and then bind to the RegisterButtonVisible property.
Using MVVM
If you want to achieve the same using MVVM (instead of on a control), you need to use a converter to convert a boolean to a visibility property, like so:
<Button Visibility="{Binding IsRegistrationAllowed, Converter={StaticResource BoolToVis}}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, that assumes that your DataContext is set up correctly and pointing at your ViewModel.

TemplatedParent Binding in Resources of ControlTemplate

here is my scenario: I have a control template for a custom control which is rather big. To encapsulate the code of one of the features of that custom control, I'd like to introduce a helper class (called Internals). This class has logic code and provides some properties, which should be used in Bindings within the ControlTemplate.
Therefore I need to create an instance of this class in XAML and bind the TemplatedParent to a dependency property of the Internals class. My problem now is a concrete binding to the object the ControlTemplate is applied on. I've created a little proof of concept:
MainWindow.xaml:
<Window x:Class="WpfProofOfConcept.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfProofOfConcept="clr-namespace:WpfProofOfConcept"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow"
Height="650"
Width="525">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.Resources>
<wpfProofOfConcept:Internals x:Key="InternalsKey"
Base="{Binding Path=., RelativeSource={RelativeSource TemplatedParent}}" />
</Grid.Resources>
<Grid.RenderTransform>
<RotateTransform Angle="20" />
</Grid.RenderTransform>
<!-- if uncommented, Binding to Base is working -->
<!--<wpfProofOfConcept:Internals Base="{Binding Path=., RelativeSource={RelativeSource TemplatedParent}}" />-->
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button>
<TextBlock>Some text</TextBlock>
</Button>
</Grid>
</Window>
Internals.cs:
public sealed class Internals : FrameworkElement
{
public static readonly DependencyProperty BaseProperty =
DependencyProperty.Register("Base", typeof(object), typeof(Internals), new PropertyMetadata((o, s) => { }));
public object Base
{
get { return (object)GetValue(BaseProperty); }
set { SetValue(BaseProperty, value); }
}
public Internals()
{
}
}
I need a reference to the concrete object the Template is applied on in my Internals object - thus the TemplatedParent binding. It does not work and I do not get any binding errors in the output.
The strange thing is that it works when the Internals object is created outside of the resources section (see commented lines in the XAML code). I do not
And one more thing thats keeps me confused: In Silverlight, binding to the TemplatedParent within a resource section works. This seems to be a WPF issue.
Do you have any ideas how to get this binding done? Or can you explain why TemplatedParent binding does not work in the resources section?
Thanks for all your hints!
It is possible to make Bindings from the Resources! But the object in resources has to be a freezable object, e.g. its class has to extend Freezable. For more information, have a look at this link from Dr. WPF http://drwpf.com/blog/2008/05/22/leveraging-freezables-to-provide-an-inheritance-context-for-bindings/:
In addition to the above scenario (where a freezable is set as a
dependency property value on a dependency object), it is this enhanced
notion of an inheritance tree and inheritance context that allows
bindings on brushes, animations, and other freezable objects to work
when those objects are placed within a resource dictionary.
You have to change the class implementation to:
public sealed class Internals : Freezable
Grüße,
Peter ;)
When you are creating Internals as Resource, it is not the part of the VisualTree and hence binding is not getting applied to it
Once your define it inside the Grid, it become the part of VisualTree.

What are the advantages/disadvantages of the different ways of binding content in a WPF UserControl to its properties?

When starting to work with WPF UserControls, I stumbled upon several ways to bind content of a UserControl to one of its properties.
Here's example C# code for my control:
public sealed partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty TheTextProperty =
DependencyProperty.Register("TheText",
typeof (string),
typeof(MyUserControl),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.
BindsTwoWayByDefault)
);
public string TheText
{
get { return (string)GetValue(TheTextProperty); }
set { SetValue(TheTextProperty, value); }
}
}
And here are the different ways I found to bind content to this property:
Content uses binding with RelativeSource/AncestorType
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<StackPanel>
<TextBox Text="{Binding TheText,
RelativeSource={RelativeSource
AncestorType={x:Type UserControl}}}" />
</StackPanel>
</UserControl>
DataContext of visual tree root is set to UserControl in XAML
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<StackPanel DataContext="{Binding
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
<TextBox Text="{Binding TheText}" />
</StackPanel>
</UserControl>
DataContext of visual tree root is set to UserControl in constructor
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<StackPanel x:Name="VisualTreeRoot">
<TextBox Text="{Binding TheText}" />
</StackPanel>
</UserControl>
Here's the constructor:
public MyUserControl()
{
InitializeComponent();
VisualTreeRoot.DataContext = this;
}
Last but not least: A warning for other people new to programming UserControls in WPF
The first time I wanted to bind content of a UserControl to one of its properties, I though "hey, let's just set the DataContext of the UserControl directly to itself":
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
Or:
public MyUserControl()
{
InitializeComponent();
this.DataContext = this;
}
However, this does not work if a user of the UserControl wants to bind its properties to other binding sources. The UserControl needs to inherit the DataContext from its parent to make this work. By overwriting it as presented above, the bindings won't find their sources anymore.
My final questions:
What are the advantages and disadvantages of each of the presented methods?
When should you use which method?
Are there more methods?
Well in the first case there is no DataContext for the TextBox set to any of it's Parent's. Hence you're having to tell the TextBox where in the VisualTree is the control with that property directly on it.
Second case DataContext is set on StackPanel which the TextBox inherit's accordingly. This is better than approach one if you have multiple control's in the StackPanel
Setting DataContext on the UserControl itself is not always wrong(via constructor or xaml). I say this because if you have 20 control's out of which 15 that need to use properties defined in it's current UserControl class and 5 that need's the parent of the UserControl's DataContext, You can always use a RelativeSource FindAncestor binding on the minority.
Only "method" I can think of that can show pt3 I mentioned is something like
<!-- Can change type to another userControl too and specify Ancestorlevel -->
<TextBlock Text="{Binding TheText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
^^ This will work fine even if the TextBlock's parent UserControl has itself as it's DataContext
As far as when to use what.
That's just a logical choice, if you have multiple siblings needing the same DataContext, Setting DataContext to their parent is the right answer. I always tend to set DataContext on the Top-most element possible and if any one or two items need variations bind them out accordingly.
If in MVVM, your VM become the DataContext almost always of the Top level item of the View. everything else Bind's directly to the element whose property they need pretty much.
i use element binding for usercontrols when binding to their own dependency properties.
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="uc">
<StackPanel>
<TextBox Text="{Binding ElementName=uc,Path=TheText}" />
</StackPanel>
</UserControl>

How to bind a image source in ControlTemplate from Code behind?

I need to bind the image source that defined in code behind into ControlTemplate. I have problem making it work correctly. I am wondering if someone can help.
The image is in ControlTemplate of the Button style. I instantiate that Button style in multiple XAML pages. My goal is to define 'Icon' instance in each code behind of these xaml pages to place the icon that is unique for each XAML page. Perphaps, it can be a better way to define unique icons that should be displayed in each XAML page where the button style will be loaded.
Below is the link to the solution I've created. It is a simple sample where I have 2 xaml pages with the buttons using the same style. I need to be able to load icon1.png into the button while mainWindow.xaml is opened and icon2.png when Page1.xaml is opened.
http://cid-0c29483cf3a6a14d.office.live.com/self.aspx/WPF%5E_Tests/BindingImageFromCode.zip
I see a couple of solutions to your problem. You'll never know what the parent of your Button really is so a code behind Binding to a property for Window, Page etc. will be hard to accomplish without to much hardcoding.
Approach 1
Subscribe to the Loaded event for the Button, find the Image in the Button Template with FindName and set the Source from there. This can also be done with an Attached Behavior
Xaml
<Button Style="{DynamicResource ButtonStyle1}"
Loaded="Button_Loaded"
...>
Code behind
private void Button_Loaded(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
Image imgIcon2 = button.Template.FindName("imgIcon2", button) as Image;
Uri uri = new Uri("Resources/icon1.png", UriKind.Relative);
ImageSource imgSource = new BitmapImage(uri);
imgIcon2.Source = imgSource;
}
Approach 2
Create a subclassed Button called e.g ImageButton where you add a new Property UriSource which you can bind to inside the Template.
<Style x:Key="ButtonStyle1" TargetType="{x:Type local:ImageButton}">
<!--...-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<!--...-->
<Image x:Name="imgIcon2"
Source="{Binding RelativeSource={RelativeSource self},
Path=UriSource}"
.../>
<!--...-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usable like
<local:ImageButton Style="{DynamicResource ButtonStyle1}"
UriSource="Resources/icon1.png"
...>
Also, I thought that you could bind to an Attached Property from inside of the ControlTemplate but that doesn't seem to be working..

Bind gui controls within a UserControl to another UserControl and its containing controls in WPF

I have UserControl1 which is a FormatButtonBar with format buttons AND
I have UserControl2 which is a DataGrid with RichTextBoxes as cell editors.
I want to reuse UserControl1 at different places of my application.
This is what I want to achieve with pseudo code:
<UserControl1>
<ToggleButton Content="bold" IsChecked="{Binding IsTextBold}" Command="EditingCommands.ToggleBold" CommandTarget="{Binding ElementName=UserControl2.MyRichTextBox}" />
</UserControl1>
<UserControl2>
<DataGrid>
<DataGridCell x:Name="MyRichTextBox" />
</DataGrid>
</UserControl2>
Do you know how the binding must look like?
You will need to define [ContentProperty( "CustomContent" )] on your UserControl class by pointing it to a custom UIElement dependency property. Then in your UserControl xaml, add a <ContentControl> and bind its Content property to your custom property like so:
<ContentControl
Content="{Binding ElementName=myUserControl, Path=CustomContent}" />

Resources