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.
Related
I'm writting an app in WP7 (Silverlight 3). I have a view model that looks like this
public class MainViewModel
{
public List<ActivityTypes> ActivityTypes{get;set;}
public RelayCommand LoadActivity{get;set;}
}
My pages datacontext is set to the view model and I have a listbox with it's item source set to the ActivityTypes collection. In the listbox I'm trying to render a list of buttons who's command is bound to the LoadActivity property on the viewmodel. RelayCommand is part of the MVVM Light toolkit in case you are wondering what it is.
The problem I am having is I can't find a way to bind my button command to the LoadActivity property as my listbox has it's itemsource set to the Activitytypes collection and this property is in the parent. I've read about FindAncester but it doesn't look like this is supported in Silverlight 3.
My XAML looks like this
<UserControl.Resources>
<DataTemplate x:Key="ActivityTypeListTemplate">
<StackPanel>
<Button Command="{Binding LoadActivity}"> <!--binding not found as we're in the collection-->
<TextBlock Text="{Binding Name}" FontSize="50"/>
</Button>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<ListBox Margin="0" ItemsSource="{Binding ActivityTypes}" ItemTemplate="{StaticResource ActivityTypeListTemplate}"/>
</Grid>
What's the best way to code something like this?
There is no "direct" way to do this. You can set the Buttons DataContext to your MainViewModel (preferably as a StaticResource link) and the it would work.
Take a look at Bind to parent object in xaml
I've encountered an oddity with a very basic WPF exercise I've devised for myself, namely dynamically populating menus from a ViewModel. Given the following main window markup:
<Window x:Class="Demosne.Client.WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:project="clr-namespace:Demosne.Client.WPF">
<Grid>
<Menu Height="26" Name="menu1" VerticalAlignment="Top" HorizontalAlignment="Stretch" ItemsSource="{Binding MainMenuItems}">
<Menu.ItemTemplate>
<HierarchicalDataTemplate >
<MenuItem Header="{Binding Text, Mode=OneTime}" ItemsSource="{Binding MenuItems}"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
<!--<MenuItem Header="File" />
<MenuItem Header="Edit" />-->
</Menu>
</Grid>
and the ViewModel(s):
public class MainWindowViewModel
{
private IList<MenuItemViewModel> _menuItems = new List<MenuItemViewModel>()
{
new MenuItemViewModel() { Text = "File" },
new MenuItemViewModel() { Text = "Edit" }
};
public IList<MenuItemViewModel> MainMenuItems
{
get
{
return _menuItems;
}
}
}
public class MenuItemViewModel
{
public string Text { get; set; }
public IList<MenuItemViewModel> MenuItems
{
get
{
return _menuItems;
}
}
private IList<MenuItemViewModel> _menuItems = new List<MenuItemViewModel>();
}
I would expect the GUI to exactly reproduce the the result of the two commented-out lines in the markup - two MenuItems called File and Edit.
However, the bound version behaves strangely on mouseover:
Markup version:
Bound version:
Why are they different?
You are getting funny results, because you are not really using the HierarchicalDataTemplate the correct way.
When you set a itemssource on a Menu, it will create a MenuItem for each object in the collection, and if you also supply a HierarchicalDataTemplate with a itemssource set, it will create MenuItems for each of the child objects in that collection as well, down the hierarchy.
In your case, you've added a MenuItem yourself in the template, which is not needed. The framework creates those items implicitly for you. And this is causing the menu to behave oddly.
So to get a correct result you should do something like this:
<HierarchicalDataTemplate ItemsSource="{Binding MenuItems}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Text}" />
</StackPanel>
</HierarchicalDataTemplate>
Update
By setting a DataTemplate on something, you are telling WPF that you want to control, how each of its items should be displayed.
In this case a HierarchicalDataTemplate is used, which is a template for generating headered controls. This kind of control contains a header and an items collection.
When you apply this kind of template to an object, whatever you have put in the template will be used as the header, and the items collection will be created by applying the template to each of the child objects in the collection set as the ItemsSource on the template. So it will recursively apply the template to all objects in the hierarchy.
In your example, you have a Menu. You could just create it by doing this:
<Menu ItemsSource="{Binding MainMenuItems}" />
It would work fine, but since you have not applied a template, to tell it how the items in the collection should be displayed, it will just create a MenuItem for each object in the itemssource and call ToString() on it. This value will then be used as the Header property on the MenuItem.
Since that not what you want, you have to apply a template, to tell WPF what you would like to be displayed as the content in the header of the implicitly generated MenuItem.
In my example I simply made a template containing a TextBlock, which binds to the Text property on the viewmodel.
Update 2
If you now want to set properties on the implicitly created menuitems, you have to that by setting the ItemContainerStyle property on the HierarchicalDataTemplate. The styled defined here will be applied to all the generated menuitems.
So to bind the Command property of the MenuItem to a Command property on the viewmodel you can do this:
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding Command}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
Try this HierarchicalDataTemplate:
<HierarchicalDataTemplate>
<MenuItem ItemsSource="{Binding MenuItems}">
<MenuItem.Template>
<ControlTemplate>
<TextBlock Text="{Binding Text, Mode=OneTime}" />
</ControlTemplate>
</MenuItem.Template>
</MenuItem>
</HierarchicalDataTemplate>
MenuItem ControlTemplate Example (msdn link)
Controls in Windows Presentation Foundation (WPF) have a
ControlTemplate that contains the visual tree of that control. You can
change the structure and appearance of a control by modifying the
ControlTemplate of that control. There is no way to replace only part
of the visual tree of a control; to change the visual tree of a
control you must set the Template property of the control to its new
and complete ControlTemplate.
OK let's see now on the visual tree.
If we have something like this:
<Menu Height="26" Grid.Row="1">
<MenuItem Header="File" />
<MenuItem Header="Edit" />
</Menu>
Visual tree of this is represented below:
Ok, so MenuItem has ContentPresenter with TextBlock.
What happens if we have HierarchicalDataTemplate ?
<Menu.ItemTemplate>
<HierarchicalDataTemplate >
<MenuItem Header="{Binding Text, Mode=OneTime}" ItemsSource="{Binding MenuItems}"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
Let's see on the visual tree:
Wow, what it is???
So if you don't specified ControlTemplate of MenuItem, it is itself ContentPresenter of the MenuItem (you can see this on the second screen). So, you must override ControlTemplate of the MenuItem if you want use it in HierarchicalDataTemplate (first screen).
Below is the visual tree with my solution:
I am using System.Windows.Interactivity.dll and Microsoft.Expression.Interaction.dll to do event handling in Viewmodel in my MVVM WPF project.
below is the code inside my Xaml:
<ItemsControl ItemsSource="{Binding Path= HeaderList}" Grid.Row="0" Grid.Column="0" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Width="100" HorizontalAlignment="Left" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<ie:CallMethodAction MethodName="PrevMouseDownEventHandler" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
for this I added namespaces in the same Xaml.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ie="http://schemas.microsoft.com/expression/2010/interactions"
and in my viewmodel I have created a method having PrevMouseDownEventHandler name which is same as that of I mentioned as CallMethod inside EventTigger in the Xaml.
On running my application when I presses mouse button on TextBlock event is generated and look for PrevMouseDownEventHandler method and leave me into following exception:
Could not find method named 'PrevMouseDownEventHandler' on object of type 'string' that matches the expected signature.
this method is as below in my ViewModel.
public void PrevMouseMoveEventHandler(object sender, MouseButtonEventArgs e)
{
// Some implementation here;
}
I don't have any idea where I am going wrong.
Except this all the functionalities inside Viewmodel is working fine for me.
what would be possible solution for this?
CallMethodAction is a delegate with no parameters and no return value. So the "handler" (really an action trigger) would have to look like this:
public void PrevMouseMoveEventHandler()
{
// Some implementation here;
}
Also, you'll need to bind to the View Model (your current binding points to the current item in the ItemsControl). You could do this using RelativeSource binding:
<ie:CallMethodAction MethodName="PrevMouseDownEventHandler"
TargetObject="{Binding Path=DataContext,RelativeSource={RelativeSource AncestorType=ItemsControl}" />
It is looking for the method on the String object you have bound your Text property to.
Basically your data context has changed from the view model to a property of the View Model.
I am working on WPF and have little knowledge of XAML customly created controls.
I have a custom control name 'DualButton' as follows :
<Controls:DualButton x:Name="StandardConferenceCancelButton"
Width="90"
Height="25"
Margin="2"
LeftButtonCommand="{Binding StandardModeConnectCommand}"
RightButtonCommand="{Binding ConferenceCancelCommand}"
>
<AccessText HorizontalAlignment="Center" Text="{x:Static I18N:TelephonyRegionViewRes.Standard}" />
</Controls:DualButton>
its 2 dependancy properties 'LeftButtonCommand' and 'RightButtonCommand'binds two different ICommands.
I want to set Visibility of this button to CanExecute of LeftButtonCommand so that when LeftButtonCommandCanExecute() returns true, that time only button gets visible.
I took dependancyProperty 'IsEnabled' what further I need to do in this
I know it's late, but might help others...
In your DataContext, create a property like IsVisible that returns
StandardModeConnectCommand.CanExecute()
In your window or user control, add the resource
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
In your dual button, after or before the commands, add:
Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
Finally, in the places where the returned value of CanEecute is likely to change, add in your datacontext a
NotifyPropertyChanged(nameof(IsVIsible));
You should be on track with this.
I have created a XAML UserControl that is used to enter the current date using some up/down controls. The interesting parts of the UserControl are as follows:
<UserControl x:Class="MyApp.Controls.DateEntry"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uControl="clr-namespace:MyApp.Controls"
xmlns:uConverters="clr-namespace:MyApp.Converters"
x:Name="dateEntry">
etc...
Here's where the numeric up/down controls are defined
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<uControl:NumericEntry x:Name="monthEntry" Label="Month" Style="{StaticResource SmallNumericEntry}" Maximum="12" Number="{Binding Path=Month, ElementName=dateEntry, Mode=TwoWay}" Minimum="1"/>
<uControl:NumericEntry x:Name="dayEntry" Label="Day" Style="{StaticResource SmallNumericEntry}" Margin="10,0,0,0" Maximum="31" Number="{Binding ElementName=dateEntry, Path=Day, Mode=TwoWay}" Minimum="1"/>
<uControl:NumericEntry x:Name="yearEntry" Label="Year" Style="{StaticResource LargeNumericEntry}" Margin="10,0,0,0" Maximum="9999" Number="{Binding ElementName=dateEntry, Path=Year, Mode=TwoWay}" Minimum="1"/>
</StackPanel>
You can see how certain properties of the NumericEntries are defined (e.g. For yearEntry, Maximum="9999"). Now what I want to do, is allow any anyone who uses this UserControl in their XAML code to be able to modify this property. Here's some XAML (seperate file) that uses this UserControl:
<uControl:DateEntry
x:Name="treatmentDate"
Date="{Binding Source={StaticResource currentTreatment}, Path=Date, Mode=TwoWay}"
Margin="10" />
I want to override the value of yearEntry.Maximum to be 2099. However, in the XAML file that uses the UserControl, it doesn't have visibility to yearEntry. It is possible to modify this programatically in the .cs file, but this kind of definition surely belongs in the XAML file.
Thanks in advance for your responses!
if your dateEntry class had a dependency property for maximum year, you could bind to them from any control that uses them. then your code to set the year would look like this
<uControl:NumericEntry
x:Name="yearEntry"
Label="Year"
Style="{StaticResource LargeNumericEntry}"
Margin="10,0,0,0"
Maximum="{Binding ElementName=dateEntry, Path=MaximumYear}"
Number="{Binding ElementName=dateEntry, Path=Year, Mode=TwoWay}"
Minimum="1"/>
and in your code behind you could set the max to 9999 in the dependency props definition
public int MaximumYear {
get { return (int)GetValue(MaximumYearProperty); }
set { SetValue(MaximumYearProperty, value); }
}
public static readonly DependencyProperty MaximumYearProperty =
DependencyProperty.Register("MaximumYear", typeof(int), typeof(NumericEntry), new UIPropertyMetadata(9999));
then use it like this
<uControl:DateEntry
x:Name="treatmentDate"
Date="{Binding Source={StaticResource currentTreatment}, Path=Date, Mode=TwoWay}"
MaximumYear="9999"
Margin="10" />
Anything you want to be externally visible on your UserControl generally should be a public property, event, etc, on that UserControl. Except in extremely rare situations clients should not have to drill down into the UserControl's "guts" to work with them.
In your case, you should have a MaximumYear DependencyProperty of type int declared in your UserControl. This is declared in the code-behind - use "wpfdp" template for VB or "propdp" for C# editor. (Type the template abbreviation and hit tab to get a fillable template).
Once your DependencyProperty has been created, your UserControl's XAML can bind to it:
<uControl:NumericEntry x:Name="yearEntry" Maximum="{Binding MaximumYear, ...
and your clients can use it as an ordinary property or in XAML:
dateEntry.MaximumYear = 2010;
or in the client code's XAML:
<uControl:DateEntry MaximumYear="2010" ...