Silverlight Nested Properties - silverlight

First Control
I can create a UserControl with a DependencyProperty called UserNameLabel. Then, I can just set the datacotext to relativesource self on the UserControl and fill the property in markup.
...
public String UserNameLabel
{
get { return (String)GetValue(UserNameLabelProperty); }
set { SetValue(UserNameLabelProperty, value); }
}
// Using a DependencyProperty as the backing store for UserNameLabel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UserNameLabelProperty =
DependencyProperty.Register("UserNameLabel", typeof(String), typeof(LoginControl), new PropertyMetadata());
<Grid x:Name="LayoutRoot">
<local:LabelTextBox Height="37" Margin="10,24,43,0" VerticalAlignment="Top" Label="{Binding UserNameLabel}"/>
</Grid>
...
Second Control
I can also create a LabelTextBox control and set the relativesource self to it with a similar Label Property.
...
public String Label
{
get { return (String)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
// Using a DependencyProperty as the backing store for Label. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(String), typeof(LabelTextBox), new PropertyMetadata(String.Empty));
...
<Grid x:Name="LayoutRoot">
<TextBlock Height="17" VerticalAlignment="Top" TextWrapping="Wrap" Text="{Binding Label}"/>
However, if I want to nest the LabelTextBox in the first usercontrol I can't seem to put a binding on the Label Property of the LabelTextBox that binds to the UserNameText Property.
It seems like a logical way to create controls where you can set the property of the parent or child control to set the child control's property.
Please help me with this.

No this is not a good approach, you should not assume you have control over any publically available property of a UserControl, that includes the DataContext.
When I want to bind a property of an element to a property of the containing UserControl I use this approach:-
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding Parent.Label, ElementName=LayoutRoot}" />
</Grid>
This uses ElementName to set the binding source to the Child of the UserControl. The Parent in the property path then finds the UserControl itself after which we can bind to whatever property that is needed, in this case the Label property.
In this approach you need not muck about with the DataContext.

Related

WPF ContentControl Content as ContentControl

Is it not possible to have multiple layers of UserControls containing ContentControl?
I am trying to create Views for different Models that are derived, so I'd like to eliminate the need to re-create the Views for each object type, and instead provide a ContentControl, or a ContentPresenter to inject the "rest of the view". However, I can only go one level deep with this method.
Here's a simplified example.(I've removed some of the xmlns). In my case, I'm working with significantly more complex Views, and trying to eliminate duplicate code in multiple places, concerned for changes later.
I have a Base UserControl, we'll call it UserControlLevel1
<UserControl x:Class="ContentControlNesting.UserControlLevel1"
x:Name="userControlLevel1"
xmlns:local="clr-namespace:ContentControlNesting">
<StackPanel>
<TextBlock Text="UserControlLevel1ContentTop"/>
<ContentControl Content="{Binding ElementName=userControlLevel1, Path=ChildContent}"/>
<TextBlock Text="UserControlLevel2ContentBottom"/>
</StackPanel>
</UserControl>
It has the following DependencyProperty on the UserControl
namespace ContentControlNesting
{
public partial class UserControlLevel1 : UserControl
{
public UserControlLevel1()
{
InitializeComponent();
}
public static readonly DependencyProperty ChildContentProperty = DependencyProperty.Register("ChildContent", typeof(UIElement), typeof(UserControlLevel1), new PropertyMetadata(null));
public UIElement ChildContent
{
get { return (UIElement)GetValue(ChildContentProperty); }
set { SetValue(ChildContentProperty, value); }
}
}
}
The ContentControl will be used in the following UserControl called UserControlLevel2. This UserControl works fine, just the way I would expect. Or rather UserControlLevel1 works properly within UserControlLevel2.
<UserControl x:Class="ContentControlNesting.UserControlLevel2"
x:Name="userControlLevel2"
xmlns:local="clr-namespace:ContentControlNesting">
<local:UserControlLevel1>
<local:UserControlLevel1.ChildContent>
<StackPanel>
<TextBlock Text="UserControlLevel2ContentTop"/>
<ContentControl Content="{Binding ElementName=userControlLevel2, Path=ChildContent}"/>
<TextBlock Text="UserControlLevel2ContentBottom"/>
</StackPanel>
</local:UserControlLevel1.ChildContent>
</local:UserControlLevel1>
</UserControl>
Likewise, it has a single DependencyProperty for the ContentControl on this UserControl like the first. I've also tried this with differently named DependencyProperties.
namespace ContentControlNesting
{
public partial class UserControlLevel1 : UserControl
{
public UserControlLevel1()
{
InitializeComponent();
}
public static readonly DependencyProperty ChildContentProperty = DependencyProperty.Register("ChildContent", typeof(UIElement), typeof(UserControlLevel1), new PropertyMetadata(null));
public UIElement ChildContent
{
get { return (UIElement)GetValue(ChildContentProperty); }
set { SetValue(ChildContentProperty, value); }
}
}
}
Okay, so at this point, everything seems to be working fine. I've added additional content inside of the ContentControl of UserControlLevel1, and I've added another ContentControl within my UserControlLevel2 UserControl.
The problem is when I try to introduce a 3rd Tier of either UserControl or my MainWindow. Anything I add to the ContentControl of UserControlLevel2 just does not appear.
<Window x:Class="ContentControlNesting.MainWindow"
xmlns:local="clr-namespace:ContentControlNesting"
Title="MainWindow" Height="200" Width="300">
<local:UserControlLevel2>
<local:UserControlLevel2.ChildContent>
<StackPanel>
<TextBlock Text="Main Window Content Text"/>
</StackPanel>
</local:UserControlLevel2.ChildContent>
</local:UserControlLevel2>
</Window>
Am I trying to do something that's not possible? Or am I doing something wrong with ContentControl and the DependencyProperties? Should I be looking at this with a different approach?
It is possible. The system cannot resolve the ElementName in the Binding. The solution is to use the relative binding. Just replace the following line in UserControlLevel2 and your are done:
<ContentControl Content="{Binding Path=ChildContent, RelativeSource={RelativeSource AncestorType={x:Type local:UserControlLevel2}}}"/>

WPF MVVM get UserControl actual position via a DependencyProperty

I have a quite common design MVVM app: MainWindow has a ContentPresenter defined like this:
<ContentPresenter Grid.Row="1" Grid.Column="1"
Content="{Binding Path=CurrentViewModel}">
</ContentPresenter>
It uses DataTemplate and can switch Views:
<DataTemplate DataType="{x:Type vm:PlateEntireViewModel}">
<v:PlateEntireView/>
</DataTemplate>
PlateEntireView is a UserControl with PlateEntireViewModel as DataContext. Now - I want to have a property in PlateEntireViewModel, that would hold PlateEntireView actual position (Left,Top) inside the MainWindow. Can this be acieved? Is it possible to make some DependencyProperty and use it in PlateEntireView, like:
<Grid ext:CustomProperties.ActualPositionX="{Binding Path=ActualPositionX, Mode=OneWayToSource}">
</Grid>
Can anybody tell me if its the right way to try - and how to use it?
So the shortest answer for this is normally the ViewModel would not care about specific coordinates that are being displayed in the View. That being said it is possible to do this relatively simple.
All you need to do is set up an attached property that will retrieve the point from the left top corner of the screen
public static double GetXCoordinate(DependencyObject obj)
{
var fe = obj as FrameworkElement;
if (fe != null)
{
return (fe.PointToScreen(new Point())).X;
}
return -1;
}
public static void SetXCoordinate(DependencyObject obj, double value)
{
obj.SetValue(XCoordinateProperty, value);
}
// Using a DependencyProperty as the backing store for XCoordinate. This enables animation, styling, binding, etc...
public static readonly DependencyProperty XCoordinateProperty =
DependencyProperty.RegisterAttached("XCoordinate", typeof(double), typeof(CustomProperties), new PropertyMetadata(0.0));
Then you could have the you binding look as such
<local:control Grid.Column="1"
Grid.Row="1"
x:Name="cp"
local:CustomProperties.XCoordinate="{Binding XCoordinate, UpdateSourceTrigger=Explicit}"
/>
You will need to update explicitly though, since this binding will never fire off any change events. You can do that by hooking into a reasonable event in your view. For more information about that look here.

Bind user control dependency properties in MVVM style Windows Phone app

I'm having some issues with binding some custom controls in a Windows Phone app right now. Usually this is never an issue but apparently my mind can't comprehend this today.
So I'm doing an MVVM style setup which is good. I have my page with a view and also a viewmodel. Now on a WebClient callback I assign the dataContext of my view to the list of models in my ViewModel, nice and simple thus far...now in my view I created a ListBox with a custom control in the datatemplate which is basically a cell in the list. I once again set my user controls dataContext to binding, and binding all the models values to the regular UI elements works no problem.
Here's a sample:
<Grid Grid.Column="0">
<Image Source="{Binding SmallPath}" VerticalAlignment="Top"/>
</Grid>
<Grid Grid.Column="1">
<StackPanel Margin="12,0,0,0">
<TextBlock x:Name="MemberId_TextBlock" Text="{Binding MemberId}" FontSize="28"
Margin="0,-8,0,0"
Foreground="{StaticResource PhoneForegroundBrush}"/>
<StackPanel Orientation="Horizontal" Margin="0,-11,0,0">
<TextBlock Text="{Binding DaysReported}" FontSize="42"
Margin="0,0,0,0"
Foreground="{StaticResource PhoneAccentBrush}"/>
<TextBlock Text="days" FontSize="24"
Margin="3,19,0,0"
Foreground="{StaticResource PhoneSubtleBrush}"/>
</StackPanel>
</StackPanel>
</Grid>
That's in my user control, and here's the the view where the usercontrol is housed:
<Grid x:Name="LayoutRoot" Background="Transparent">
<ListBox Name="TopSpotter_ListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<!--<TextBlock Text="{Binding MemberId}"/>-->
<controls:TopSpotterItemControl DataContext="{Binding}"/>
<Grid Height="18"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Now this is good enough but what I want to do in my view is set data from my model like Booleans that determine whether or not I should show certain Grids etc. So if I try to set a dependency property explicitly in my control it fires and will run logic in the Getter/Setters for instance. HOWEVER if I try to set these custom objects from a binding source it won't actually set.
Here's what works:
<controls:TopSpotterItemControl ChampVisibility="True">
This way will trigger the ChampVisibility property and then in the code behind of the user control I can set visibilities.
Here's what fails but I want to work:
<controls:TopSpotterItemControl ChampVisibility="{Binding IsChamp">
In addition I can still set the DataContext to {Binding} and the result will be unchanged.
In this scenario IsChamp is part of my model that I would like to bind to this user control which I guess comes from the dataContext being set on the view from the viewModel. I'm not sure what I can do to get this so the bindings work etc. without having to set custom properties.
Finally, here's my user control:
public partial class TopSpotterItemControl : UserControl
{
public string MemberId
{
get
{
return this.MemberId_TextBlock.Text;
}
set
{
this.MemberId_TextBlock.Text = value;
}
}
public bool ChampVisibility {
set
{
if (value)
{
this.Champ_Grid.Visibility = System.Windows.Visibility.Visible;
}
}
}
public static readonly DependencyProperty MemberNameProperty =
DependencyProperty.Register("MemberId", typeof(string), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public TopSpotterItemControl()
{
InitializeComponent();
}
}
Bit long winded and I hope I made things on the issue clear. My one major hang up so far, and I'd like to abstract as much control as I can to the user control via dependency properties explicitly set in xaml, rather than setting up binding in its xaml that depend on the knowledge of a model. Thanks!
Your DependencyProperty is badly formed. (I also don't see Champ_Grid defined in your class or XAML, but I assume that is an ommission)
Setting ChampVisibility = true in code works because it is unrelated to the DependencyProperty.
You can tell easily because the default value for your DP is invalid. It will compile, but the instance constructor will through an exception if it is ever invoked.
new PropertyMetadata(null)
bool = null = exception
If you call GetValue(TopSpotterItemControl.ChampVisibilityProperty) from somewhere you can confirm all of the above.
You should make changes to instance fields in the property changed handler and declare the property like the following, it will work:
Note that the property has to change (not just be set) for the event to be raised.
public bool ChampVisibility
{
get { return (bool)GetValue(ChampVisibilityProperty); }
set { SetValue(ChampVisibilityProperty, value); }
}
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility ", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(true, (s, e) =>
{
TopSpotterItemControl instance = s as TopSpotterItemControl;
instance.Champ_Grid.Visibility = instance.ChampVisibility ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}));
Incidentally, your MemberId DependencyProperty is also completely wrong and cannot work.
Note:
The Binding on your TextBox works, because it is binding to the DataContext (your model), so it probably shows the right value.
The Dependency property in your UserControl will never be set though.
Use the propdp code-snippet in Visual Studio so you dont have to concern yourself with the complexities of Dependency Property declaration.
Also check this out for more info about Dependency Properties

Making an easy to use UserControl via Properties

in my Silverlight 4 app, I try to create a simple UserControl, which will be consumed by my Application. To keep things simple, it shall have a "header" and a placeholder, where I want to place any kind of control.
<User Control ...>
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="TextBlockHeader" Text="{Binding Title}" />
<ContentPresenter x:Name="ContentPresenterObject" />
</Grid>
</UserControl>
In the code behind, I have created a property for the text of the TextBlock
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(MyAccordion), null);
This way, I can set the Title property, when I use the Control in my application.
<local:MyAccordion Title="Test"/>
But it seems, that the binding at the textblock Text="{Binding Title}" doesn't make the text "Test" to be displayed as the textblocks text.
My question is: How can I make the Property Title to be displayed as the textboxes text and how do I do this for the - any type of user control containable - contencontrol?
Thanks in advance,
Frank
Maybe DataContext of control or page was not set. - First of all you should read more about a Binding ("http://www.silverlight.net/learn/data-networking/binding/data-binding-to-controls-(silverlight-quickstart)"). If you are working on real project and will design a some arhitecture, you should read about MVVM pattern.
The answer is ElementPropertyBinding. I need to reference the User Control in the Binding or add the binding in the constructor.
Create the binding in XAML:
<User Control ... x:Name="userControl">
...
<TextBlock x:Name="TextBlockHeader" Text="{Binding Title, ElementName=userControl}" />
</UserControl>
Create the binding in the constructor (Code behind)
public MyUserControl()
{
// Required to initialize variables
InitializeComponent();
TextBlockHeader.SetBinding(TextBlock.TextProperty, new System.Windows.Data.Binding() { Source = this, Path = new PropertyPath("Title") });
}
I still need to find out how to add a child control, but that's another question.

Silverlight: Why can't designer see the dependency property?

I have to create Silverlight User Control with public properties that should be used in inner controls.
public partial class MyControl : UserControl
{
public static readonly DependencyProperty MyCustomProperty =
DependencyProperty.Register(
"MyCustom", typeof(string), typeof(MyControl),
new PropertyMetadata("defaultValue"));
public string MyCustom
{
...
}
I tried several ways to bind, but all fail - dependency property is not seen for some reason.
For example this straightforward binding fails:
<UserControl x:Class="...MyControl"
...
x:Name="mc"
>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Image Source="{Binding Path=MyCustom, Mode=OneWay, ElementName=mc}" />
</Grid>
</UserControl>
What I'am doing wrong?
What you are doing is not a good pattern. The UserControl does not really "own" the name property. If another UserControl or Page were to place an instanced of your MyControl in its Xaml, it can give it name other than "mc", at which point your code is broken.
Instead use this approach:-
<UserControl x:Class="...MyControl"
>
<Grid x:Name="LayoutRoot" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Image Source="{Binding Path=Parent.MyCustom, Mode=OneWay, ElementName=LayoutRoot}" />
</Grid>
</UserControl>
That out of the way you main problem is that the Image Source property is of type ImageSource not string. You get to use a string literal in Xaml because the Xaml parser does some parser magic the converts the string to an ImageSource. This doesn't happen when using binding.
Change you controls property to:-
public partial class MyControl : UserControl
{
public static readonly DependencyProperty MyCustomProperty =
DependencyProperty.Register(
"MyCustom", typeof(ImageSource), typeof(MyControl),
new PropertyMetadata(null));
[TypeConverter(typeof(ImageSourceConverter))]
public ImageSource MyCustom
{
...
}
Now in another UserControl or Page where you MyControl is hosted you can use a string to assign this MyCustom property. However in code you need to create an instance of something like BitmapImage to assign to this property.

Resources