Setting Properties a Control in xaml - wpf

I am creating a custom WPF control that uses an image inside of it. This custom control will be like any other, it will be declared in xaml. I want to have a public property for this control to specify the source of the internal image, much the same way you do this when using an Image control:
<Image Source="http://foo.com/bar.jpg"></Image>
What I want to do is have the following usage of my control:
<MyCustomControl ImageSource="http://foo.com/bar.jpg"></MyCustomControl>
And then internally, something like:
<UserControl class="MyCustomControl" ...>
<Image Source="{Binding Imagesource}"></Image>
</UserControl>
What kind of setup do I need in my codebehind to get this to work? i've tried a few things but can get nothing to work.

What you need is a dependency property of type ImageSource and a proper binding, either use ElementName or RelativeSource, do not use the DataContext on UserControls.
<UserControl Name="control" x:Class="MyCustomControl" ...>
<Image Source="{Binding ImageSource, ElementName=control}"/>
</UserControl>
<UserControl x:Class="MyCustomControl" ...>
<Image Source="{Binding ImageSource,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>

Related

ElementName not working when assigning DataTemplate from code behind?

I am trying to access Control using ElementName from DataTemplate that is used in different UserControl (Resources) than defined (in xaml).
Imagine this situation:
MyUserControl.xaml with following DataTemplate in resources:
<UserControl.Resources>
<DataTemplate x:Key="SomeTemplate">
<TextBlock Text="{Binding Text, ElementName=TextElement}"/>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="TextElement" Text="IT WORKS! (not...)"/>
</Grid>
</UserControl>
MyUserControlWrapper.xaml
<ContentPresenter x:Name="ContentPresenter" Content="{Binding SomeContent}"/>
and in code behind of MyUserControlWrapper.xaml i set ContentTemplate of ContentPresenter from MyUserControl.xaml:
something like:
ContentPresenter.ContentTemplate = (DataTemplate)childView.Resources["SomeTemplate"];
Is it possible to use ElementName from resources that are defined outside UserControl?
How DataTemplate searches for ElementName in same UserControl then? Maybe its possible to set something like DataContext for DataTemplate itself for ElementName to work, without messing with DataContext that is sent to controls used inside Template?
You need to review the concepts related to Namescopes.
Briefly names are scoped at the point where a Xaml resources are loaded. For example each UserControl will each load their own Xaml and therefore have their own namescope. In your case you asking MyUserControlWrapper to find a name that its LoadComponent has not seen.
Maybe you can just walk up the VisualTree using RelativeSource and FindAncestor?
There is a nice presentation of different binding variants here:
http://www.wpfwiki.com/Default.aspx?Page=WPF%20Q5.3&AspxAutoDetectCookieSupport=1

What exactly does WPF Data Binding's "RelativeSource FindAncestor" do?

I am currently working within a WPF user control (the root element of my XAML file is "UserControl"), which I know is being hosted inside a Window. How can I access a property of the Window using data binding?
Does anyone know why simply
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" Path="..." />
does not work? The error message I get is:
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''.
Edit: I ended up using a variation on ArsenMkrt's approach, so have accepted his answer. However, I am still interested in finding out why FindAncestor does not "just work".
The best way is to give a name to UserControl
Create dependency property MyProperty in UserControl with two way binding and bind it in main Window, than bind in UserControl like this
<UserControl x:Name = "myControl">
<Label Content={Binding ElementName= myControl, Path=MyProperty}/>
</UserControl>
If you're trying to 'escape' from an ItemsControl or DataGridView to get to a Window you may be finding that AncestorType of x:Type Window doesn't work. Or at least doesn't seem to...
If this is the case you're probably running Blend or Visual Studio and expecting the data to be visible at design time - which it won't because VS + Blend both create their own instances that aren't really Windows. It will work at runtime just fine, but not during design mode.
There's a couple things you can do:
Wrap in a UserControl
Here's an alternative solution I've come up with. It has one advantage in that you're not referencing a UserControl or Window directly, so if you change the parent container your code won't break.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:MyWPFApplication.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyWPFApplication.Views.UPCLabelPrinterWindow"
mc:Ignorable="d"
x:Name="LayoutRoot"
Title="UPCLabelPrinterWindow">
<views:DataContextWrapper>
<DockPanel>
...
</DockPanel>
</views:DataContextWrapper>
Where DataContextWrapper is just a Grid
namespace MyWPFApplication.Views {
public class DataContextWrapper : Grid
{
}
}
Then when you bind you do this :
<TextBlock Text="{Binding="{Binding DataContext.SomeText,
RelativeSource={RelativeSource AncestorType={x:Type views:DataContextWrapper},
Mode=FindAncestor}}" />
Note: if you want to bind to a property ON Window itself it's trickier and you should probably bind via a dependency property or something like that. But if you are using MVVM then this is one solution I found.
I Think You Should SET Mode="OneWayToSource" Like this:
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor ,AncestorType={x:Type Grid}},Path=BackGround , Mode=OneWayToSource , UpdateSourceTrigger = PropertyChanged}" />
If you are using a view model as your Window's DataContext and the property you need to bind to is from that view model then you should prefix the path with DataContext.MyPropertyPath, something like this:
<TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}"/>
this translates as "Find me an ancestor window and then look in it's data context for MyProperty"

Can I get strongly typed bindings in WPF/XAML?

Using the MVVM-pattern you set the DataContext to a specific ViewModel. Now is there any way to tell the XAML the type of the DataContext so that it will validate my bindings?
Looking for something like the typed viewdata in ASP.NET MVC.
You can write each individual binding in a strongly-typed way:
<TextBox Text="{Binding Path=(vm:Site.Contact).(vm:Contact.Name)}" />
However this would not validate the fact that TextBox DataContext is of type ViewModel.Site (and I think this is not possible, but I may be wrong).
No, the current spec does not have strong typing in Xaml. I believe that with .Net 4.0, Xaml should be seeing the capacity for generics. With that, I would think it should be much easier to have strong typing in Xaml.
No. FrameworkElement.DatatContext is the dependency property that enables data binding is of type object.
As pointed out by others, you can specify the expected type of a DataContext for a special template called a DataTemplate. Many controls such as ItemsControl, ControlControl provide access to DataTemplates to allow you to set the visual representation's expectations of the DataContext's type.
Bryan is correct, he did not test his code.
The correct application of a typed DataTemplate looks like this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" />
</Window>
ContentPresenter inherits directly from FrameworkElement and does not have a Template property. In addition, the Template property commonly refers to Control.Template of type ControlTemplate which is something entirely different than a DataTemplate.
I think Bryan was thinking of the ContentControl which is one of the two root control types (the other being ItemsControl). ContentControl does in fact inherit from Control. Therefore we can specify the Template property on it if we so choose.
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
<ControlTemplate x:Key="ControlSkin" TargetType="{x:Type ContentControl}">
...
</ControlTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" Template="{StaticResource ControlSkin}" />
</Window>
I personally declare a static PropertyPath for each property in my viewmodel the reference this using x:static as the binding path -
e.g
public class MyViewModel
{
public static PropertyPath MyPropertyPath = new PropertyPath("MyProperty");
public bool MyProperty{get; set;}
}
xaml : {Binding Path={x:Static local:MyViewModel.MyPropertyPath}}
This way all my bindings get validated on build.
Try this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding}" Template="{StaticResource TypedTemplate}" />
</Window>
I haven't tested this code but it should give you the idea. The content presenter will display the current DataContext which will use the DataTemplate. This isn't strongly typed in the compiler but will throw a runtime error immediately on load (in the window's InitializeComponent). You should be able to catch this easily in your testing if something breaks.

Silverlight - Setting DataContext in XAML rather than in constructor?

How can I set the DataContext on my Grid in XAML, instead of in the constructor?
Here is how I do it in the constructor (LayoutRoot is the XAML Grid defined in the XAML):
this.LayoutRoot.DataContext = this.HPVM;
I would prefer to do it right in the XAML, but I do not know how to reference the HPVM object in XAML. HPVM is a public property on the USerControl class.
It works fine as listed above, but again, I just want to know how to properties of the UserControl class in XAML, rather than always having to do it in code.
Here is all the relevant code:
<UserControl x:Class="SilverlightApplication1.SLHolePattern" x:Name="HolePatternsControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"
xmlns:local="clr-namespace:SilverlightApplication1"
xmlns:GeoPatterns="clr-namespace:GeoPatterns"
Height="700">
<UserControl.Resources>
...
And here is my constructor where the DataContext is currently set:
namespace SilverlightApplication1
{
public partial class SLHolePattern : UserControl, INotifyPropertyChanged
{
public HolePatternsViewModel HPVM;
public SLHolePattern()
{
InitializeComponent();
this.HPVM=new HolePatternsViewModel();
this.LayoutRoot.DataContext = this.HPVM;
...more code here
}
It all works fine, but I just want to learn how to set the DataContext in XAML, not in code.
The answer Chris gave works just fine.
I have tested and it worked for me.
You can instantiate your class in XAML (within the UserControl.Resources) and
then bind the datacontext to a static resource.
Follow code:
<UserControl ...>
<UserControl.Resources>
<myNS:MyClass x:Name="TheContext" x:Key="TheContext"></myNS:MyClass>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource TheContext}" >
<TextBlock Text="{Binding Path=Field1}">
</TextBlock>
</Grid>
</UserControl>
The following monstrosity works in Silverlight 4
<UserControl
DataContext="{Binding HPVM, RelativeSource={RelativeSource Self}}">
<UserControl.DataContext>
<vm:ThisUCViewModel />
</UserControl.DataContext>
try something like this.....
<Grid DataContext="{Binding Path=HPVM}">
</Grid>
where HPVM is a public member of this--> your form etc.
Create the instance of your class in the xaml, by adding something like this to your resources section.... (don't forget to add your xmlns namespace)
<my:bogart x:Key="franken"/>
then, bind the data context to the static resource you just added....
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource franken}">
<TextBox Background="Red" Foreground="White" Text="{Binding Path=sum}" />
</Grid>
In Silverlight 4, I was able to get this working by doing the following:
Give the Page/UserControl an x:Name="myPage"
In your control binding use normal Element bidning syntax. In my case I want to bind to an observable collection of objects in my code behind for my ItemsSource property:
<ComboBox
ItemsSource={Binding ElementName=myPage, Path=MyObservableObjectList, Mode=TwoWay}
I haven't tried this with DataContext but know you can do element to element binding for DataContext as I do this for Grids whose context is based on the selected item of some other drop down on the page.
This is not possible (It is possible in WPF with {Binding RelativeSource={RelativeSource Self}}, but Silverlight is more limited.
You have to do it through code.
<UserControl.Resources>
<ResourceDictionary>
<vm:YourModelx:Key="myModel"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource myModel}"/>
</UserControl.DataContext>

How do I bind resource strings in XAML to multiple attributes within the same control?

Is it possible to bind an additional resource string to another attribute within a control. I already have one attribute bound to a resource but also need another. I can't see a way of doing it as I would need an extra DataContext but oviously can only have one.
The additional attribute I need to bind to is Content within the hyperlink control.
The xaml file is as follows:-
<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mystuff="clr-namespace:my.assembly.name;assembly=my.assembly.name"
Width="100" Height="100">
<UserControl.Resources>
<mystuff:TxtResConv x:Key="TxtResConv" />
<mystuff:TxtResPar x:Key="LabelTitle" ResourceUri="LabelTitle" DefaultValue="default label title" />
<mystuff:TxtResPar x:Key="LinkURL" ResourceUri="LinkURL" DefaultValue="default label title" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<HyperlinkButton DataContext="{StaticResource LinkURL}" x:Name="HyperLink1" Content="NEED TO ADD RESOURCE STRING LABELTITLE HERE !!" NavigateUri="{Binding Mode=OneWay,Converter={StaticResource TxtResConv}}"></HyperlinkButton>
</Grid>
</UserControl>
Thanks in advance.
You use the Binding syntax to bind to the Content property in the same way you bind to the NavigateUri property. If it comes from a different source than LinkUri then you specify that in the binding syntax Source property:
Content="{Binding Source={StaticResource LabelTitle},
Converter={StaticResource TxtResConv}}"

Resources