I have a custom usercontrol with DataContext="{Binding RelativeSource={RelativeSource self}}"
On the code behind i've made a dependency property like:
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) => { new Base().OnPropertyChanged("ElementName"); })));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
Now when I try to use this usercontrol in my mainpage.xaml and use the following binding: <test.TestControl ElementName="{Binding name}" />, it keeps searching for 'name' property in my custom usercontrol instead of where it should come from?
What am I doing wrong ?
It searches there because you have the DataContext set on the topmost level for your user control. What you would need to do is get rid of the relative binding to self in the user control and specify ElementName in bindings (inside user control). Btw you probably don't need OnPropertyChanged in the PropertyChangedCallback cause DependencyProperties in their nature notify about value changes.
I eventually solved it this way. Not the way I wanted, but it's a (in my eyes) pretty neat solution.
CustomUserControl.xaml
<UserControl x:Class="TestApp.Controls.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Width="75"
Height="75">
<Canvas x:Name="LayoutRoot"
Background="Black">
<StackPanel Orientation="Vertical">
<Image x:Name="UCImage"
Width="50"
Height="50"
HorizontalAlignment="Center" />
<TextBlock x:Name="UCText"
HorizontalAlignment="Center" />
</StackPanel>
</Canvas>
</UserControl>
CustomUserControl.xaml.cs
public partial class ElementControl : UserControl
{
#region DependencyProperty ElementNameProperty
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) =>
{
//See Here
((ElementControl)s).UCText.Text = e.NewValue as string;
})));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
#endregion
}
Related
I'm trying to set the fill property of several instances of the same usercontrol from XAML in order to distinguish them. I'm using a dependency property in the C# codebehind of the control and referring to that in the XAML when I instantiate the control. Here's a simplified example of what I've tried, first the XAML of the user control:
<UserControl x:Class="RectangleFillUserControlTest.RectangleFillTest"
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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="150">
<Grid>
<Rectangle x:Name="rect" HorizontalAlignment="Left" Height="50" Stroke="Black" VerticalAlignment="Top" Width="150"/>
</Grid>
</UserControl>
Now the codebehind:
namespace RectangleFillUserControlTest
{
public partial class RectangleFillTest : UserControl
{
SolidColorBrush fillBrush;
public static readonly DependencyProperty FillColourProperty = DependencyProperty.Register
("FillColour", typeof(string), typeof(RectangleFillTest), new PropertyMetadata(string.Empty));
public string FillColour
{
get { return (string)GetValue(FillColourProperty); }
set
{
SetValue(FillColourProperty, value);
if (value == "red") fillBrush = new SolidColorBrush(Colors.Red);
else fillBrush = new SolidColorBrush(Colors.Green);
rect.Fill = fillBrush;
}
}
public RectangleFillTest()
{
InitializeComponent();
}
}
}
I instantiate the control in the main window and try to set the fill colour to red:
<Window x:Class="RectangleFillUserControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RectangleFillUserControlTest"
Title="MainWindow" Height="350" Width="525">
<Grid Background="#FF1D2CC3">
<local:RectangleFillTest FillColour="red"/>
</Grid>
</Window>
But the rectangle remains unfilled, even when I run the project. Can anyone help please?
Cheers,
Tim
There are two things wrong with your dependency property.
First, its type should be Brush, not string, because that is the type used by properties of WPF controls like Shape.Fill or Control.Background. WPF provides automatic type conversion from strings like "Red" or "#FFFF0000" in XAML to type Brush.
Second, you should not have anything else than a call to SetValue in the setter method of the CLR wrapper. The reason is explained in the XAML Loading and Dependency Properties article on MSDN:
Because the current WPF implementation of the XAML processor behavior
for property setting bypasses the wrappers entirely, you should not
put any additional logic into the set definitions of the wrapper for
your custom dependency property. If you put such logic in the set
definition, then the logic will not be executed when the property is
set in XAML rather than in code.
So your dependency property declaration should look like this:
public static readonly DependencyProperty FillBrushProperty =
DependencyProperty.Register(
"FillBrush", typeof(Brush), typeof(RectangleFillTest));
public Brush FillBrush
{
get { return (Brush)GetValue(FillBrushProperty); }
set { SetValue(FillBrushProperty, value); }
}
To react to property changes, you would now register a PropertyChangedCallback with property metadata. But you don't need to do that here, because you could simply bind the property in the UserControl's XAML like this:
<Rectangle Fill="{Binding FillBrush,
RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" ... />
I will explain why is is not working and how to solve.
1.- A Dependency Property is only called when the usercontrol has that dependency property in the visual tree.
In case you want to do in that way, you need to add for instance :
new PropertyMetadata(string.Empty, ValueChanged));
and there change the value:
public static readonly DependencyProperty FillColourProperty = DependencyProperty.Register
("FillColour", typeof(string), typeof(RectangleFillTest), new PropertyMetadata(string.Empty, ValueChanged));
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as RectangleFillTest;
var fillBrush = new SolidColorBrush();
if (control.FillColour == "red")
fillBrush = new SolidColorBrush(Colors.Red);
else
fillBrush = new SolidColorBrush(Colors.Green);
control.rect.Fill = fillBrush;
}
public string FillColour
{
get
{
return (string)GetValue(FillColourProperty);
}
set
{
SetValue(FillColourProperty, value);
}
}
That is explicit for your logic, in case you need a more generic code for any color, etc using binding the property to the rectangle, just tell me.
You need to bind your Dependency Property to the Fill property of your Rectangle in the xaml of your UserControl. You'll have Something like this :
<Rectangle x:Name="rect" Fill="{Binding FillColour, RelativeSource={RelativeSource FindAncestor, AncestorType=RectangleFillTest}}" HorizontalAlignment="Left" Height="50" Stroke="Black" VerticalAlignment="Top" Width="150"/>
Also, in your dependency property, it's type should be Brush, and not String.
I need ListBox with my UserControl listed in it. My UserControl has TextBox. So I want to display property of List's subitem in UserControl's textBox. I have tried a lot of options with DataContext and ElementName - it just doesn`t work. I just stucked on it. The only way to make it work is to remove DataContext binding of UserControl to itself and change Item Property name so it matches to DependencyProperty name - but I need to reuse my control in different viewmodels with different entities so it is almost not possible to use the approach.
Interesting thing is that if I change my UserControl to Textbox and bind Text property of it - everything works. What the difference between Textbox and my UserControl?
So let me just show my code.
I have simplified the code to show only essential:
Control XAML:
<UserControl x:Class="TestControl.MyControl"
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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBlock Text="{Binding Text}"/>
</Grid>
</UserControl>
Control CS:
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public string Text
{
get {
return (string)this.GetValue(TextProperty); }
set {
this.SetValue(TextProperty, value); }
}
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl), new propertyMetadata(""));
}
Window XAML:
<Window x:Class="TestControl.MainWindow"
Name="_windows"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestControl"
Title="MainWindow" Height="350" Width="525" >
<Grid Name="RootGrid">
<ListBox ItemsSource="{Binding ElementName=_windows, Path=MyList}">
<ItemsControl.ItemTemplate >
<DataTemplate >
<local:MyControl Text="{Binding Path=Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Grid>
</Window>
Window CS:
public partial class MainWindow : Window
{
public MainWindow()
{
_list = new ObservableCollection<Item>();
_list.Add(new Item("Sam"));
_list.Add(new Item("App"));
_list.Add(new Item("H**"));
InitializeComponent();
}
private ObservableCollection<Item> _list;
public ObservableCollection<Item> MyList
{
get { return _list;}
set {}
}
}
public class Item
{
public Item(string name)
{
_name = name;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
This is a pretty big gotcha in XAML. The problem is that when you do this in the user control:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
You change its data context, so that in this line:
<local:MyControl Text="{Binding Path=Name}"/>
The runtime will now attempt to resolve "Name" on the instance of "MyControl", instead of on the inherited data context (ie, the view model). (Confirm this by checking the Output window -- you should see a binding error to that effect.)
You can get around this by, instead of setting the user control's data context that way, using a RelativeSource binding:
<UserControl x:Class="TestControl.MyControl"
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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200"
<Grid>
<TextBlock
Text="{Binding Text,RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</Grid>
</UserControl>
Here is the XAML for my user control:
<UserControl x:Name="titledTextBox" x:Class="VitalStats.View.Controls.TitledTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480"
>
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="titleTextBlock"
TextWrapping="Wrap"
Margin="12,5,0,-5"
Text="{Binding Title, ElementName=titledTextBox, FallbackValue=Title Here}"
FontSize="20"
Foreground="{StaticResource PhoneSubtleBrush}"/>
<TextBox x:Name="inputTextBox" Text="{Binding Text, ElementName=titledTextBox, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</UserControl>
and this is what my code-behind looks like,
// Usings here
namespace VitalStats.View.Controls
{
public partial class TitledTextBox : UserControl
{
[Description("A TextBox with built in title")]
public TitledTextBox()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(this) )
{
this.Title = "Title Here";
}
}
public string Title
{
get { return this.GetValue(TitleProperty) as string; }
set { this.SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TitledTextBox), null);
public string Text
{
get { return this.GetValue(TextProperty) as string; }
set { this.SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TitledTextBox), null);
}
}
The binding work when reading data into the UI (so the Title property works OK) however
when reading from th UI (i.e. trying to access Text from the code) the property is always null implying that the binding is only one-way (despite the Mode=TwoWay property).
I am aware (thanks to XamlZealot's answer) of the FindAncestor binding, however AncestorType does not exist in Windows Phone 7 (or Silverlight) XAML namespace.
How then do I set up a two-way binding to a UserControl property from within the UserControl?
This is on Windows Phone 7 (7.1 project).
Try a RelativeSource Binding in lieu of the ElementName:
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=Title, FallbackValue=Title Here}"
So the following is perhaps not what I asked but what I used to solve the problem. Since all I wanted to do was link the text of the title TextBlock to Title on the UserControl and the same for the text in the input, the code behind can look like this,
public string Title
{
get { return this.titleTextBlock.Text; }
set { this.titleTextBlock.Text = value; }
}
public string Text
{
get { return this.inputTextBox.Text; }
set { this.inputTextBox.Text = value; }
}
No need for DependencyProperties etc.
A UserControl has 3 Dependency Properties: FormatAvailabilities, Orientation and FullText. FormatAvailabilities is bound to the ItemsSource property of an ItemsControl. Orientation is bound to the Orientation property if the StackPanel which is in the ItemsPanelTemplate within the ItemsControl. FullText is bound to the Visibility property of two TextBlocks inside the DataTemplate of the ItemsControl. I am using two converters to determine which TextBlock to show: a BoolToVisibilityConverter and a BoolToInvertedVisibilityConverter (the latter is an inversion of the former). I copied the Visibility property as-is from the TextBlock (both of them, independently) to the ItemsControl and it works correctly..
It seems that the bindings on the TextBlocks are not working properly because both are always visible. Since they are both binding on the same property but one is inverted, there should never be a possibility for both to be visible at the same time.
I put a breakpoint in my converter and it is never hit, so my guess is that there is an issue with binding from within a repeating control to the outer control in which it is housed.
App.xaml:
<common:BaseApp x:Class="xyz.App" xmlns:converters="clr-namespace:xyz.Converters;assembly=xyz">
<common:BaseApp.RootVisual>
<phone:PhoneApplicationFrame x:Name="RootFrame" Source="/Home.xaml"/>
</common:BaseApp.RootVisual>
<common:BaseApp.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<converters:BoolToVisibilityConverter x:Key="BoolToInvertedVisibilityConverter" IfTrue="Collapsed" IfFalse="Visible"/>
</common:BaseApp.Resources>
</common:BaseApp>
UserControl XAML:
<UserControl
x:Name="FormatsControl"
x:Class="xyz.Formats"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<ItemsControl Background="Transparent" ItemsSource="{Binding ElementName=FormatsControl, Path=FormatAvailabilities}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="{Binding ElementName=FormatsControl, Path=Orientation}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding BindsDirectlyToSource=True}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToVisibilityConverter}}"/>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToInvertedVisibilityConverter}}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
UserControl CS:
namespace xyz
{
public partial class Formats : UserControl
{
public static readonly DependencyProperty FormatAvailabilitiesDependencyProperty = DependencyProperty.Register("FormatAvailabilities", typeof(FormatAvailability[]), typeof(Formats), null);
public static readonly DependencyProperty OrientationDependencyProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Formats), new PropertyMetadata(Orientation.Horizontal));
public static readonly DependencyProperty FullTextDependencyProperty = DependencyProperty.Register("FullText", typeof(bool), typeof(Formats), null);
public FormatAvailability[] FormatAvailabilities
{
get { return (FormatAvailability[])base.GetValue(Formats.FormatAvailabilitiesDependencyProperty); }
set { base.SetValue(Formats.FormatAvailabilitiesDependencyProperty, value); }
}
public Orientation Orientation
{
get { return (Orientation)base.GetValue(Formats.OrientationDependencyProperty); }
set { base.SetValue(Formats.OrientationDependencyProperty, value); }
}
public bool FullText
{
get { return (bool)base.GetValue(Formats.FullTextDependencyProperty); }
set { base.SetValue(Formats.FullTextDependencyProperty, value); }
}
public Formats()
{
InitializeComponent();
}
}
}
I must be over looking something...thanks!
There is an issue with naming UserControls in Silverlight 3 as described by this blog post, which is also present in the Windows Phone 7 version of Silverlight. Effectively, if you give the UserControl a name in the XAML where it is used (i.e. it's parent), then that overrides the name given in the UserControl's own XAML file.
I ran into a similar problem, instead of binding to the elementname I changed the binding to this
Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}
And that works just fine.
Looks like you are missing the OnPropertyChanged handler.
Here is one of my dependency properties. Note the changed handler.
public ObservableCollection<ObjWithDesc> ItemsSource
{
get
{
return (ObservableCollection<ObjWithDesc>)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(ObservableCollection<ObjWithDesc>),
typeof(HorizontalListBox),
new PropertyMetadata(OnItemsSourcePropertyChanged)
);
static void OnItemsSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((HorizontalListBox) obj).OnItemsSourcePropertyChanged(e);
}
private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
{
ObservableCollection<ObjWithDesc> objWithDescList = (ObservableCollection<ObjWithDesc>)e.NewValue;
MainListBox.ItemsSource = objWithDescList;
}
How can I access the public variable which in Sample.xaml.cs file like asp.net <%=VariableName%>?
There are a few ways to do this.
Add your variable as a resource from codebehind:
myWindow.Resources.Add("myResourceKey", myVariable);
Then you can access it from XAML:
<TextBlock Text="{StaticResource myResourceKey}"/>
If you have to add it after the XAML gets parsed, you can use a DynamicResource above instead of StaticResource.
Make the variable a property of something in your XAML. Usually this works through the DataContext:
myWindow.DataContext = myVariable;
or
myWindow.MyProperty = myVariable;
After this, anything in your XAML can access it through a Binding:
<TextBlock Text="{Binding Path=PropertyOfMyVariable}"/>
or
<TextBlock Text="{Binding ElementName=myWindow, Path=MyProperty}"/>
For binding, if DataContext is not in use, you can simply add this to the constructor of the code behind:
this.DataContext = this;
Using this, every property in the code becomes accessible to binding:
<TextBlock Text="{Binding PropertyName}"/>
Another way is to just give a name to the root element of the XAML:
x:Name="root"
Since the XAML is compiled as a partial class of the code-behind, we can access every property by name:
<TextBlock Text="{Binding ElementName="root" Path=PropertyName}"/>
Note: access is only available to properties; not to fields. set; and get; or {Binding Mode = OneWay} are necessary. If OneWay binding is used, the underlying data should implement INotifyPropertyChanged.
For quick-and-dirty Windows in WPF, I prefer binding the DataContext of the Window to the window itself; this can all be done in XAML.
Window1.xaml
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource self}}"
Title="Window1" Height="300" Width="300">
<StackPanel>
<TextBlock Text="{Binding Path=MyProperty1}" />
<TextBlock Text="{Binding Path=MyProperty2}" />
<Button Content="Set Property Values" Click="Button_Click" />
</StackPanel>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public static readonly DependencyProperty MyProperty2Property =
DependencyProperty.Register("MyProperty2", typeof(string), typeof(Window1), new UIPropertyMetadata(string.Empty));
public static readonly DependencyProperty MyProperty1Property =
DependencyProperty.Register("MyProperty1", typeof(string), typeof(Window1), new UIPropertyMetadata(string.Empty));
public Window1()
{
InitializeComponent();
}
public string MyProperty1
{
get { return (string)GetValue(MyProperty1Property); }
set { SetValue(MyProperty1Property, value); }
}
public string MyProperty2
{
get { return (string)GetValue(MyProperty2Property); }
set { SetValue(MyProperty2Property, value); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Set MyProperty1 and 2
this.MyProperty1 = "Hello";
this.MyProperty2 = "World";
}
}
In the above example, note the binding used in the DataContext property on the Window, this says "Set your data context to yourself". The two text blocks are bound to MyProperty1 and MyProperty2, the event handler for the button will set these values, which will automatically propagate to the Text property of the two TextBlocks as the properties are Dependency Properties.
It is also worth noting that a 'Binding' can only be set on a DependencyProperty of a DependencyObject. If you want to set a non DependencyProperty (eg. a normal property) on an object in XAML, then you will have to use Robert's first method of using resources in the code behind.
myWindow.xaml
<Window
...
<TextBlock Text="{ Binding Path=testString }" />
</Window>
myWindow.xaml.cs
public partial class myWindow: Window
{
public string testString { get; set; } = "This is a test string";
public myWindow()
{
DataContext = this;
InitializeComponent();
}
}
Important
Set Datacontext
testString MUST be public
testString MUST be a property (have a get and set)