I had a large controle (bunch of labels etc.) that I used that often I wanted to extract that as an UserControl.
Working Code Before:
<Image Source="/Images/MainWindow/Label.png" Width="20" Height="20" />
Code Now:
New User Control xaml:
<Image Source="{Binding ImageSource}" Width="20" Height="20" />
UserControl Property:
public ImageSource ImageSource { get; set; }
Call of UC:
<uc:TitleUC x:Name="TitleBar" ImageSource="/Images/MainWindow/Label.png" />
Sadly the image doesn't show and I can't find any information why not or how to debug it.
The Binding in the UserControl's XAML should use the UserControl instance as source object, not the current DataContext:
<Image Source="{Binding ImageSource,
RelativeSource={RelativeSource AncestorType=UserControl}}" .../>
You should also declare the ItemsSource property as dependency property, in order to make it bindable and to be able to set it by a Setter in a Style or a Trigger.
Related
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.
I need some quick help which is a road blocker for me now. I have Button in ItemsControl and I need to perform some task on Button click. I tried adding Command to Button in ItemsControl DataTemplate but its not working. Can anyone suggest how to proceed further.
<UserControl.Resources>
<DataTemplate x:key="mytask">
<TextBox Grid.Row="5" Grid.Column="2" Text="{Binding Path=PriorNote}" Grid.ColumnSpan="7" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,5" Width="505" Foreground="Black"/>
<StatusBarItem Grid.Row="2" Grid.Column="8" Margin="8,7,7,8" Grid.RowSpan="2">
<Button x:Name="DetailsButton" Command="{Binding CommandDetailsButtonClick}">
</DataTemplate>
</UserControl.Resources>
<Grid>
<ItemsControl Grid.Row="1"
ItemsSource="{Binding ListStpRules}"
ItemTemplate="{StaticResource myTaskTemplate}" Background="Black"
AlternationCount="2" >
</ItemsControl>
</Grid>
and in ViewModel I have implemented code for Command. And its not working. Please suggest any solution for me to proceed further
The DataContext of each item in your ItemsControl is the item in the collection the ItemsControl is bound to. If this item contains the Command, your code should work fine.
However, this is not usually the case. Typically there is a ViewModel containing an ObservableCollection of items for the ItemsControl, and the Command to execute. If this is your case, you'll need to change the Source of your binding so it looks for the command in ItemsControl.DataContext, not ItemsControl.Item[X]
<Button Command="{Binding
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}},
Path=DataContext.MyCommand}" />
If your ViewModel has a property of type ICommand you can bind the Button's Command property to that:
XAML:
<DataTemplate DataType="{x:Type my:FooViewModel}">
<Button Content="Click!" Command="{Binding Path=DoBarCommand}" />
</DataTemplate>
C#:
public sealed class FooViewModel
{
public ICommand DoBarCommand
{
get;
private set;
}
//...
public FooViewModel()
{
this.DoBarCommand = new DelegateCommand(this.CanDoBar, this.DoBar);
}
}
Read this:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
Implement a class similar to RelayCommand in the above article. Would make your further MVVM coding easier. :-)
Just a guess. Is CommandDetailsButtonClick defined in a ViewModel, which is DataContext of your UserControl (the one with ListStpRules property)?
DataContext of button in ItemTemplate is an item from ListStpRules, and if you command is not there then binding won't find it.
You can check diagnostic messages from wpf in Output window while debugging your application. It writes there if it can not resolve binding.
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>
I wrote simple code like
public ObservableCollection<string> Names …
public Window1()
{
PutInDataIntoNames();
InitializeComponent();
this.listBox1.ItemsSource = Names;
}
and in xaml
<Grid>
<ListBox Margin="10,11,10,16"
Name="listBox1"
Background="Black"
Foreground="Orange"
/>
</Grid>
Then I wanted to set ItemsSource property in xaml. In order to do that I wrote the following:
ItemsSource="{Binding Path=Names}"
Unfortunately, it doesn’t work. Could you explain why and how to do that right?
If you only specify the binding path the binding engine will try to navigate the path starting from the current DataContext so ItemsSource="{Binding Path=Names}" does not work like this, there are a lot of different things to keep in mind especially when doing more complex things.
The single most important article that everyone who is new to DataBinding should read is the Data Binding Overview on MSDN
To get back to your binding, if you want to do it completely in XAML you can do that as well, you just need to make the Window your source somehow, either by referencing it directly or relatively or by setting it up as the DataContext.
1 - Direct Reference:
<Window Name="Window"
...>
<Grid>
<ListBox ...
ItemsSource="{Binding ElementName=Window, Path=Names}"
.../>
</Grid>
</Window>
2 - Relative Reference
<Grid>
<ListBox ...
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Names}"
.../>
</Grid>
3 - Setting up the DataContext
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<Grid>
<ListBox ...
ItemsSource="{Binding Path=Names}"
.../>
</Grid>
</Window>
Do this in code behind
public Window1()
{
PutInDataIntoNames();
InitializeComponent();
DataContext = this;
}
and in XAML
<Grid>
<ListBox ItemsSource="{Binding Names}"
Margin="10,11,10,16"
Name="listBox1"
Background="Black"
Foreground="Orange"
/>
</Grid>
Ideally you should follow MVVM design to isolate data from code behind.
It seems that your Names might be a field. You can ONLY bind to public properties
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" ...