Silverlight 4 MVVM: Unable to Databind ICommand - silverlight

I have created a Silverlight User Control. The markup is:
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" Width="Auto" Margin="5">
<Button Content="OK" Margin="0,0,5,5" MinWidth="50" Command="{Binding OKCommand}" />
</StackPanel>
The code behind declares a Dependency property 'OKCommand' as:
public ICommand OKCommand
{
get
{
return (ICommand)GetValue(OKCommandProperty);
}
set
{
SetValue(OKCommandProperty, value);
}
}
public static readonly DependencyProperty OKCommandProperty
= DependencyProperty.Register("OKCommand", typeof(ICommand), typeof(TestUserControl), new PropertyMetadata(null, OKCommandProperty_PropertyChangedCallback));
private static void OKCommandProperty_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
Now I want to use the user control on another page where which is the View & the ViewModel defines the command to which I want the OKCommand to be bound. The XAML markup is as such:
<local:TestControl OKCommand="{Binding Path=TestControlOk}"/>
However when I click the button it does not execute anything. Any clues as to what I am doing wrong here.

You need to show the view model that contains the TestControlOk property so we can tell if that's part of the problem.
UserControls do not register themselves as the data context automatically so the binding inside the user control won't have anything to bind to. Do you have
this.DataContext = this;
anywhere in the UserControl codebehind to enable your first binding to actually work?
Alternatively, you can do something like so:
<UserControl .....
x:Name="MyUserControl">
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" Width="Auto" Margin="5">
<Button Content="OK" Margin="0,0,5,5" MinWidth="50"
Command="{Binding OKCommand, ElementName=MyUserControl}" />
</StackPanel>
</UserControl>
Note the ElementName= part of the binding pointing to the root UserControl element in your XAML.

Related

Bind header and content dynamically to expander

Firstly I see the type for Header and content for the expander, it says the type is object. I have a user control with name CommonExpanderUserControl as follows,
xaml:
<uwpControls:Expander Header="{Binding HeaderContent}" Content="{Binding MainContent}">
</uwpControls:Expander>
In xaml.cs (DataContext is set to this)
public static readonly DependencyProperty HeaderContentProperty =
DependencyProperty.Register("HeaderContent", typeof(object), typeof(CommonExpanderUserControl), new
PropertyMetadata(null));
public object HeaderContent
{
get { return (object)GetValue(HeaderContentProperty); }
set { SetValue(HeaderContentProperty, value); }
}
public static readonly DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(ContentControl), typeof(CommonExpanderUserControl), new
PropertyMetadata(null));
public ContentControl MainContent
{
get { return (ContentControl)GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
Now I am using this UserControl somewhere outside as follows,
<UserControl.Resources>
<ContentControl x:Key="Header">
<Grid x:Name="ExpanderHeaderGrid" HorizontalAlignment="Stretch" Padding="0" Margin="0"
Background="{Binding LisSharedSettings.ChangeHeaderColor,Converter={StaticResource BoolToSolidBrushConverter}}">
<TextBlock x:Name="TextBlockLisSharedSettingsTitle"
x:Uid="/Application.GlobalizationLibrary/Resources/InstrumentSettingsViewLisSettingsTextBlockTitle"
Style="{StaticResource TextBlockStyleSectionHeader}"/>
</Grid>
</ContentControl>
<ContentControl x:Key="Body">
Some content here.
</ContentControl>
</UserControl.Resources>
<Grid>
<local:CommonExpanderUserControl HeaderContent="{StaticResource Header}" MainContent="{StaticResource Body}"/>
</Grid>
Binding Content control like that simply doesn't work. If I remove the MainContent binding and bind only the Header, it says object reference not set to an instance of an object. Please help.
The problem occurs StaticResource binding, we could not bind header with control by StaticResource. And for the scenario the better way is bind HeaderTemplate and send the data source to the header property like the following.
<UserControl.Resources>
<DataTemplate x:Key="HeaderTemplate">
<Grid
x:Name="ExpanderHeaderGrid"
Margin="0"
Padding="0"
HorizontalAlignment="Stretch"
Background="Red"
>
<TextBlock x:Name="TextBlockLisSharedSettingsTitle" Text="{Binding}" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<uwpControls:Expander Header="hello" HeaderTemplate="{StaticResource HeaderTemplate}" />
</Grid>

C# WPF What's wrong with this template?

I'm trying to create a new window (child of MainWindow) that will display different numbers of rows with data each time. I used the following template that I copied from a wpf template tutorial. I've checked that the data are right and I can project them right with a MessageBox.Show window but I can't make them appear as bound properties. Here is the xaml...
<ListView x:Name="listUnits" x:FieldModifier="public" HorizontalAlignment="Left" Height="Auto" Margin="5,5,5,5" VerticalAlignment="Top" Width="Auto">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id:"/>
<TextBlock Text="{Binding pu_Id}" Width="Auto"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding pu_unitName}" Width="Auto"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
So although pu_unitName contains the data, they're are displayed in the new window.
The binding is done in the constructor:
public partial class ChooseUnit : Window
{
public ChooseUnit(List<XML_Handler.PrUnits> thisList)
{
InitializeComponent();
listUnits.ItemsSource = thisList;
}
}
The PrUnits is a class which contains variables including pu_id and pu_unitName. So what's wrong with this code?
Since you can only bind to public properties, you need to make sure that pu_Id and pu_UnitName are properties (and not fields) of the PrUnits class. Then your bindings will work.
Also note that this is not a binding:
listUnits.ItemsSource = thisList;
In general, you would set the DataContext of the parent window to an instance of a view model class and bind the ItemsSource property of the ListView to a property that returns the List<XML_Handler.PrUnits>:
<ListView x:Name="listUnits" ItemsSource="{Binding Units}">
...
In your .cs:
public partial class ChooseUnit : Window
{
public ChooseUnit(List<XML_Handler.PrUnits> thisList)
{
InitializeComponent();
this.DataContext = thisList;
}
}
What this does it assigns the data context of your window to the list of itemsa you are trying to pass. At this point every element in that window will have access to the passed in list.
And then in your XAML do this:
<ListView x:Name="listUnits" ItemsSource="{Binding .}" x:FieldModifier="public" HorizontalAlignment="Left" Height="Auto" Margin="5,5,5,5" VerticalAlignment="Top" Width="Auto">
This allows us to use binding as the entire window has access to the list. Hence ItemsSource="{Binding .}".
EDIT
Reading more into your question and thanks to Ash your class needs to expose Properties with INotifyPropertyChanged interface implemented.
This interface is expected by the WPF framework so it can subscribe to the changed events and update the UI.

Binding disappears on simple user control

I have a simple usercontrol with a single DependencyProperty. I am unable to set bindings on this property. I don't get any exceptions but the bindings just disappear.
I cannot begin to see what is going wrong here. It's so simple.
Here's my usercontrol:
XAML:
<UserControl x:Class="Test.Controls.SimpleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ucThis">
<TextBox Text="{Binding Path=MyString, ElementName=ucThis}" />
</UserControl>
Code:
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
}
public string MyString
{
get { return (string)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(string),
typeof(SimpleUserControl), new UIPropertyMetadata("simple user control"));
}
XAML from a test app:
<StackPanel>
<testControls:SimpleUserControl MyString="{Binding Path=TestString}"
x:Name="simpleUC" />
<Label Content="From control" />
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1"
Visibility="{Binding Path=MyString, ElementName=simpleUC, Converter={StaticResource nullVisConv}}">
<ContentPresenter Content="{Binding Path=MyString, ElementName=simpleUC}" />
</Border>
<TextBlock Text="Value from control is null."
Margin="5"
Visibility="{Binding Path=MyString, ElementName=simpleUC, Converter={StaticResource nullVisConv}, ConverterParameter={custom:BooleanValue Value=True}}" />
<Label Content="From binding" />
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1"
Visibility="{Binding Path=TestString, Converter={StaticResource nullVisConv}}">
<ContentPresenter Content="{Binding Path=TestString}" />
</Border>
<TextBlock Text="Value from binding is null."
Margin="5"
Visibility="{Binding Path=TestString, Converter={StaticResource nullVisConv}, ConverterParameter={custom:BooleanValue Value=True}}" />
<TextBox Text="You can set focus here." />
</StackPanel>
The main window for the test app has a property named TestString, is its own DataContext and implements INotifyPropertyChanged correctly. SimpleUserControl.MyString updates as it should but the property it is bound to (TestString) does not. I have inspected this with Snoop; the binding I set on the SimplerUserControl is just not present at run time. What is happening here?
UPDATE
Okay. So if I specify Mode=TwoWay the binding works. That's great. Can anyone explain to me why it behaves this way?
Thanks.
Working as designed :). DPs default to 1-way. Personally, I would change your DependencyProperty.Register() call to make the DP default to two-way. That way you don't have to specify two-way explicitly every time you use it. You'll notice that the framework typically makes DPs two-way by default when you'd want the property to write back. Just a convienience.
Yes you need to provide TwoWay Mode for Dependency Property:
public string MyString
{
get { return (string)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(string),
typeof(UserControl1), new FrameworkPropertyMetadata("simple user control", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Binding with DependencyProperty

I've defined a class name TextColumns.cs, which has a DependencyProperty RichTextBlockContentProperty:
public static readonly DependencyProperty RichTextBlockContentProperty =
DependencyProperty.Register("RichTextBlockContent", typeof(string),
typeof(RichTextColumns), new PropertyMetadata(""));
public string RichTextBlockContent
{
get { return (string)GetValue(RichTextBlockContentProperty); }
set //Debug, but the SetValue won't fire
{
SetValue(RichTextBlockContentProperty, value);
}
}
In the XAML, I use it as
<FlipView x:Name="flipView"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}">
<FlipView.ItemTemplate>
<DataTemplate x:Name="myDataTemplate">
<UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates">
<ScrollViewer x:Name="scrollViewer" Style="{StaticResource HorizontalScrollViewerStyle}" Grid.Row="1">
<!-- Content is allowed to flow across as many columns as needed -->
<common:RichTextColumns x:Name="richTextColumns" Margin="117,0,117,47"
RichTextBlockContent="{Binding title}">
<RichTextBlock x:Name="richTextBlock" Width="560" Style="{StaticResource ItemRichTextStyle}">
<Paragraph>
<Run x:Name="RunText" FontSize="26" FontWeight="SemiBold" Text="{Binding title}"/>
</Paragraph>
</RichTextBlock>
</common:RichTextColumns>
</UserControl>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
When the page loaded, it's supposed that the RichTextBlockContent will get the value of the Binding "title", while the Binding in the RichTextBlock worked.
Is there something I've missed?
The setter won't get called. If you need to do logic when the value gets set you need to supply a PropertyChanged callback in the PropertyMetadata Constructor
http://msdn.microsoft.com/en-us/library/ms557330.aspx

Sending UIElements via DependencyProperty in wpf

I have a user control with a DependencyProperty that takes a UIElement. So far, so good, the problem is I cannot find the element's children.
I think the problem is my lack of knowledge, could anyone tell me what the problem is and a possible solution?
I have made a small test-program like this
Usercontrol codebehind:
public UIElement TestSendUiElement
{
get { return (StackPanel)GetValue(TestSendUiElementProperty); }
set { SetValue(TestSendUiElementProperty, value); }
}
public static readonly DependencyProperty TestSendUiElementProperty =
DependencyProperty.Register("TestSendUiElement", typeof(StackPanel), typeof(Test), new FrameworkPropertyMetadata(TestSendUiElementPropertyChanged));
private static void TestSendUiElementPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(VisualTreeHelper.GetChildrenCount((UIElement)e.NewValue));
}
xaml using the usercontrol:
<my:Test >
<my:Test.TestSendUiElement>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBox Height="23" Width="50" Margin="0,0,5,0" />
<TextBox Height="23" Width="125" />
</StackPanel>
</my:Test.TestSendUiElement>
</my:Test>
Output is 0 children. Shouldn't it be 2?
The content is no Initialized so count the object on initialization
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
Console.WriteLine(VisualTreeHelper.GetChildrenCount((UIElement)e.NewValue));
}
And you will get count 2
I think it doesn't work because whatever you assign to the TestSendUiElement DependencyProperty, it won't be part of the VisualTree. So VisualTreeHelper.GetChildrenCount(...) will not work.
As a direct replacement, LogicalTreeHelper should do the trick.
And if you know the type of the object or can , then it's even better to use exposed properties like ItemsControl.Items, ContentControl.Content and etc., with the exception of classes inheriting from Panel (they're LogicalChildren property is internal).
If you are lazy you could also do the following (untested code):
<my:Test.TestSendUiElement>
<ItemsControl Margin="0,2">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<TextBox Height="23" Width="50" Margin="0,0,5,0" />
<TextBox Height="23" Width="125" />
<ItemsControl>
</my:Test.TestSendUiElement>
Then you change the type of the DP property to ItemsControl, and now you can access the children via this.TestSendUIElement.Items. An ItemsControl is probably not as lightweight as a panel, but using the LogicalTreeHelper probably wouldn't be optimal either. Depends on the scenario.

Resources