I have a User Control A which contains within it another User Control B.
User Control A has a dependency property DeviceColour. This determines the Colour of the internal User Control B.
The registration of this Dependency property is as follows:
public static readonly DependencyProperty DeviceColourProperty = DependencyProperty.Register("DeviceColour",
typeof(vhBase.DeviceColours),typeof(ucBaseDevice),new FrameworkPropertyMetadata(vhBase.DeviceColours.Gray,
FrameworkPropertyMetadataOptions.AffectsRender,new PropertyChangedCallback(DeviceColourProperty_PropertyChanged)));
public vhBase.DeviceColours DeviceColour
{
get { return (vhBase.DeviceColours)GetValue(DeviceColourProperty); }
set { SetValue(DeviceColourProperty, value); }
}
private static void DeviceColourProperty_PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
ucBaseDevice device = (ucBaseDevice)obj;
device.VisualDevice.DeviceColour = (vhBase.DeviceColours)e.NewValue;
}
In the above property change event, VisualDevice is a property that points to the Internal User Control B as shown below:
public override vhBase VisualDevice
{
get { return vhMain; }
}
vhMain is User Control B which I can see and manipulate in the XAML designer.
The problem I'm having at design time, is that VisualDevice appears to be returning NULL i.e. vhMain, the user control is null and therefore "object reference not set to an instance....".
This works at runtime, but I need it to also work at design time.
As you've perhaps realised, I don't know too much about DP and, well, WPF in general and I'm finding myself going around in circles.
Can anyone please help?
Thanks
I've make a simple sample of what-i-think-is-right way to bind child's property to parent's one.
I used the Color property as an example, but that's the same for every propery type.
There are two user controls: ControlB with DP 'Colour' and ControlA with DP DeviceColour. ControlB just painting its background to the specified color:
<UserControl x:Class="WpfApplication1.ControlB" <!--skipped--> Name="controlB">
<Grid>
<Grid.Background>
<SolidColorBrush Color="{Binding ElementName=controlB, Path=Colour}" />
</Grid.Background>
</Grid>
</UserControl>
And there is how to bind that properties from XAML in ControlA:
<UserControl x:Class="WpfApplication1.ControlA" <!--skipped--> Name="controlA">
<Grid>
<my:ControlB Colour="{Binding ElementName=controlA, Path=DeviceColour}" />
</Grid>
</UserControl>
And there is how to use ControlA:
<Window x:Class="WpfApplication1.MainWindow" <!--skipped-->>
<Grid>
<my:ControlA DeviceColour="Green" />
</Grid>
</Window>
Related
I am currently working on a MVVM project that uses a Window (with my ViewModel) and my own UserControl. The UserControl is nearly empty in the .xaml file because all of its functionality comes from code-behind, which draws different shapes. I wanted to bind a property from ViewModel to a DependencyProperty in the UserControl, but no matter what I do, i cannot get it to work. I have read tons of answers here and on different websites and noticed that it might be something with the UserControl's DataContext, but I eventually failed to fix the problem anyway. The way I raise the PropertyChanged event in my ViewModel is correct. I can successfully bind my property to other controls (like TextBoxes etc.), but not to my one. I would be grateful if you could explain to me why it is not working and how to fix that. Regards!
MainWindow.xaml binding:
<Grid Margin="10">
<local:FretboardControl Grid.Row="0" Fretboard="{Binding CurrentFretboard, Mode=TwoWay}"/>
</Grid>
FretboardControl.xaml:
<UserControl ...>
<Grid>
<TextBlock Text="{Binding Path=Fretboard, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:FretboardControl}}"/>
//the TextBlock above is just a test
<Canvas.../>
</Grid>
</UserControl>
FretboardControl.xaml.cs (code-behind):
public static readonly DependencyProperty FretboardProperty = DependencyProperty.Register
(nameof(Fretboard), typeof(Fretboard), typeof(FretboardControl), new PropertyMetadata(new Fretboard(), PropertyChangedCallback));
public Fretboard Fretboard {
get {
return GetValue(FretboardProperty) as Fretboard;
}
set {
SetValue(FretboardProperty, value);
}
}
protected static void PropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e) {
//breakpoint here. It is reached only once during runtime:
//at start, when the default value is inserted
if (o is FretboardControl) {
(o as FretboardControl).RefreshFretboard();
}
}
Okay, so apparently i probably found the seed of my problem. My CurrentFretboard setter raised the PropertyChanged event, but i did not change the reference of the object itself. The object was modified, but it was still the same object. I thought that this would not matter and it would be sent anyway to the binding, but it looks like the PropertyChangedCallback is called only if the reference was changed. I guess i can replace the reference on each set or just listen to the PropertyChanged event already in the UserControl. Thanks for help!
The question is not about how to get the stuff working, it already does; it's about some strange behavior I'm experiencing, and I need to understand it. I have a ResourceDictionary that contains some styles, one of them got TargetType="{x:Type UserControl}" and x:Key="UCStyle"; that one is applied on multiple UserControls in the project.
Some of these UserControls got string State property in their ViewModel to be used to apply Visual States (through an external class, and an attached property, bound to the ViewModel in XAML). Till this point everything was perfect, then, I tried to add DependencyProperty State to the UserControl, and simply bind it to the state property in the ViewModel, my attempt was:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--ResourceDictionary Source="..."/-->
</ResourceDictionary.MergedDictionaries>
<Style x:Key="MyStyle" TargetType="{x:Type local:MyUserControl}" BasedOn="{StaticResource UCStyle}">
<Setter Property="State" Value="{Binding State, Mode=TwoWay}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.Style>
<DynamicResourceExtension ResourceKey="MyStyle" />
</UserControl.Style>
This worked perfectly at the runtime, but in the design-time, it always underline these lines
And shows an error says:
'MyUserControl' TargetType doesn't match type of element 'UserControl'.
And doesn't apply neither UCStyle nor MyStyle in the XAML Viewer in Visual Studio, and doesn't even draw the child UserControls properly. I didn't expect the solution to run properly, but it did!
Now my questions are:
Why does it show these errors in the design-time while it runs properly?
How to get rid of these errors in the design-time? (I cleaned, and re-built the solution, and restarted Visual Studio, and none of these worked)
What's the best practice to deal with `UserControl` Visual States in such situation in MVVM?
What's the best practice to bind a DependencyProperty of a UserControl to a property in it's ViewModel in MVVM?
I'm using Visual Studio 2012.
The wpf designer is nefarious for showing bogus errors at design time. You can't do much but ignore them.
Visual states are a concern of the UI, and therefore should be contained within the UI. MVVM does not mean no codebehind. Use your codebehind for UI tasks, and put your business logic in your view models.
Your question suggests you're creating custom view models to hold view logic for your user controls. Seriously, don't do that. That'll get you in trouble down the road. It interferes with how databinding is designed to work.
There is no "best practice" for binding user control elements to properties defined on its surface. It depends. Using a style to do this seems odd, however. You can simply give the root of the UserControl an x:Name="root" and then use ElementName=root in your binding.
An example of binding within a UserControl to a property defined on the UserControl (taken from an old prototype)...
Here's a UserControl designed to add or delete a list of stuff.
DependencyProperties defined on the UserControl
Bindings within the UserControl that bind to these properties
I don't guarantee this works, but it will illustrate how it's done:
public partial class ItemsEditor : UserControl
{
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(IEnumerable<Item>),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public IEnumerable<Item> Items
{
get { return (IEnumerable<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
#endregion
#region AddItem
public static readonly DependencyProperty AddItemProperty =
DependencyProperty.Register(
"AddItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand AddItem
{
get { return (ICommand)GetValue(AddItemProperty); }
set { SetValue(AddItemProperty, value); }
}
#endregion
#region RemoveItem
public static readonly DependencyProperty RemoveItemProperty =
DependencyProperty.Register(
"RemoveItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand RemoveItem
{
get { return (ICommand)GetValue(RemoveItemProperty); }
set { SetValue(RemoveItemProperty, value); }
}
#endregion
public ItemsEditor()
{
InitializeComponent();
}
}
It just lists a bunch of things, you can add a new thing or delete a thing from the list. Here's the bindings in xaml
<UserControl x:Class="LolPrototype.ItemsEditor"
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"
xmlns:t="clr-namespace:UCsAndICommands"
x:Name="root">
<UserControl.Resources>
<DataTemplate DataType="{x:Type t:Item}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveItem, ElementName=root}"
CommandParameter="{Binding}">Remove</Button>
<TextBox Text="{Binding Name}" Width="100"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Button Command="{Binding AddItem, ElementName=root}">Add</Button>
<ItemsControl ItemsSource="{Binding Items, ElementName=root}" />
</StackPanel>
</UserControl>
Obviously, you can define DataTemplates outside the list in an ancestor's resources. The point is to show how ElementName bindings can be used to bind against properties defined in the UserControl.
In my WPF app, I've to show different controls in the window for different user actions. Like, when user selects an image, I show a Image control and when the user selects a text file, I show a TextBox control. Similarly there are many controls for different user selections.
To do this I'm first doing Visibility = Visibility.Collapsed for all controls using a foreach loop, then doing Visibility = Visibility.Visible for the controls that I've to show.
Is there a more efficient way of doing this? The window flickers and is not really snappy when changing controls as there are many controls.
I always use a ContentControl for this. It produces very clean xaml code and is superfast, I've never seen any flicker even in rather loaded views. It does require any extra frameworks beyond WPF.
<ContentControl Content="{Binding Selected}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewModels:FirstOne}">
<!-- View code for first view goes here -->
<TextBlock>Hi</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:SecondOne}">
<!-- View code for second view goes here -->
<Image Source="{Binding Image}" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
And then I have a MainViewModel handing the navigation with a property
class MainViewModel: ViewModel, INavigation
{
public ViewModel Selected
{
get { return _selected; }
private set
{
_selected = value;
RaisePropertyChanged();
}
public void Show(ViewModel viewModel) { Selected = viewModel; }
}
}
The INavigation interface is just something like so
interface INavigation { void Show(ViewModel viewModel); }
An example view model
class FirstOne: ViewModel
{
private readonly INavigation _navigation;
public FirstOne(INavigation navigation) { _navigation = navigation; }
public void ButtonClicked()
{
_navigation.Show(new SecondOne());
}
}
First, you may want to avoid doing Visibility=Visibility.Collapsed for controls that you are about to make visible. That could reduce some unnecessary screen activity.
Second, rather than having the user experience all the individual visibility changes in a serial fashion, you might want to keep your controls in a parent control that you can hide before the visibility changes take place and then unhide once the changes are complete. You could even use an opacity animation to fade it out and then back in. Of course, without seeing or understanding more about your app, I can't tell what's really appropriate.
I'm developing a Windows Phone 8 application with an User Control.
This user control has a border, and I want to create a DependencyProperty to access this border:
public partial class CustomOptionButton : UserControl
{
public Border OuterBorder
{
get
{
return (Border)GetValue(OuterBorderProperty);
}
set
{
SetValue(OuterBorderProperty, value);
}
}
public readonly DependencyProperty OuterBorderProperty =
DependencyProperty.Register("OuterBorder", typeof(Border), typeof(CustomOptionButton), null);
But I don't know how I can bind this property in XAML.
When I have bind a TextBlock.Text, I did this on XAML:
<TextBlock x:Name="CustomText" Text="{Binding ButtonText, ElementName=userControl}" />
But, How can I do the same with the entire Border?
<Border x:Name="OutBorder" BorderBrush="White" BorderThickness="2" Margin="0">
I will need to change the BorderBrush and the BorderThickness (and maybe another properties), so I have thought that I could have only one property instead of three or four.
In this case, you need to create for each type of separate property, because to use one property Border need to create a separate Control like this:
public сlass MyBorderControl : Border
{
// Your implementation of Control
}
If you want to create a property that could be used for any Control, in WPF has attached properties:
MSDN: Attached properties overview
and used like this:
<Canvas>
<Button Canvas.Left="50">Hello</Button>
</Canvas>
In this case, Canvas.Left attached property used for the Button class.
I'm trying to have a user control where an image is passed in from its containing element. The purpose is so that I can reuse a common set of visual elements while only changing the image. For example:
The control usage:
<DataTemplate DataType={x:Type myType}>
<local:MyControl PlotIconSource="..\Images\Scatter.png"/>
</DataTemplate>
The Image inside the control
<UserControl x:Class="MyControl">
<Image Source="{Binding PlotIconSource}"/>
</UserControl>
Finally the dependency property for PlotIconSource in the code-behind for MyControl.xaml.cs.
public ImageSource PlotIconSource
{
get { return (ImageSource)GetValue(PlotIconSourceProperty); }
set { SetValue(PlotIconSourceProperty, value); }
}
public static readonly DependencyProperty PlotIconSourceProperty =
DependencyProperty.Register(
"PlotIconSource",
typeof(ImageSource),
typeof(PlotHeader),
new UIPropertyMetadata());
I'm sure I've missed something along the way so any help would be appreciated.
You might want to bind via RelativeSource or with ElementName:
<UserControl x:Class="MyControl" Name="control">
<Image Source="{Binding PlotIconSource, ElementName=control}"/>
</UserControl>
(Do not set the DataContext, it will be invisible from the outside and mess with bindings meant for an inherited DataContext)
Looks right to me, are you getting an error message or something?