The following code works by having the combobox populated by the C# code behind at run-time.
I would like to populate the user control combos declaratively from XAML, similar to how the description label is populated.
This code is just for my personal use, so I won't be using complex MVVM models. I'd ideally like a solution that passes string arrays from the main XAMLcode, to the user control as I'd like to know how to do that.
Thanks in advance :-)
//Main Window.xaml
<Window
x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="Car Details"
SizeToContent="WidthAndHeight">
<StackPanel>
<local:myControl
x:Name="myMake"
myDescription="Make" />
<local:myControl
x:Name="myModel"
myDescription="Model" />
<local:myControl
x:Name="myYear"
myDescription="Year" />
</StackPanel>
</Window>
//MainWindow.xaml.cs
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//myMake is the name of a specific myControl
//myOption is the name of the combo within myControl
myMake.myOptions.ItemsSource = new string[] { "Ford", "Toyota" };
}
}
}
//myControl.xaml
<UserControl
x:Class="WpfApp1.myControl"
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:local="clr-namespace:WpfApp1"
mc:Ignorable="d">
<StackPanel>
<Label
Content="{Binding myDescription,FallbackValue=description}"
/>
<ComboBox
x:Name="myOptions"
Width="120" />
</StackPanel>
</UserControl>
//myControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace WpfApp1
{
public partial class myControl : UserControl
{
public myControl()
{
InitializeComponent();
this.DataContext = this;
}
public string myDescription
{
get { return (string)GetValue(myDescriptionProperty); }
set { SetValue(myDescriptionProperty, value); }
}
public static readonly DependencyProperty myDescriptionProperty = DependencyProperty.Register("myDescription", typeof(string), typeof(myControl), new PropertyMetadata(null));
}
}
You can declare an array of string in xaml like this:
<x:Array xmlns:s="clr-namespace:System;assembly=mscorlib" x:Key="myStringArray" Type="{x:Type s:String}">
<s:String>Ford</s:String>
<s:String>Toyota</s:String>
</x:Array>
Source: this answer
So your view becomes:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="Car Details" SizeToContent="WidthAndHeight">
<Window.Resources>
<!--Declaration of an array of string which is added to the ressources of the window-->
<x:Array xmlns:s="clr-namespace:System;assembly=mscorlib" x:Key="myStringArray" Type="{x:Type s:String}">
<s:String>Ford</s:String>
<s:String>Toyota</s:String>
</x:Array>
</Window.Resources>
<StackPanel>
<!--We get the array from the ressource by calling "StaticResource" with the key of the element to obtain-->
<local:myControl
x:Name="myMake" myDescription="Make" ArrayItemsSource="{StaticResource myStringArray}" />
<!--Do the same for the other usercontrol -->
<local:myControl
x:Name="myModel"
myDescription="Model" />
<local:myControl
x:Name="myYear"
myDescription="Year" />
</StackPanel>
In your usercontrol, you should not use this.DataContext = this; as this is bad practice since it prevents your usercontrol from inheriting the datacontext of the view (I know you are not planning on using MVVM but I think this should still be mentioned for other readers).
So because the datacontext is not the control, the bindings in the xaml of the usercontrol have to be modified like this: {Binding ElementName=myControlName, Path=theNameOfThePropertyIwantToBind}
I created a new dependency property to pass the array of string from the view so the usercontrol becomes:
Xaml:
<UserControl x:Class="WpfApp1.myControl"
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:local="clr-namespace:WpfApp1" x:Name="myControlName"
mc:Ignorable="d">
<StackPanel>
<Label
Content="{Binding ElementName=myControlName, Path=myDescription,FallbackValue=description}"
/>
<ComboBox
x:Name="myOptions" ItemsSource="{Binding ElementName=myControlName, Path=ArrayItemsSource}"
Width="120" />
</StackPanel>
The code behind:
public partial class myControl : UserControl
{
public myControl()
{
InitializeComponent();
//Bad practice
//this.DataContext = this;
}
public string myDescription
{
get { return (string)GetValue(myDescriptionProperty); }
set { SetValue(myDescriptionProperty, value); }
}
public string[] ArrayItemsSource
{
get { return (string[])GetValue(ArrayItemsSourceProperty); }
set { SetValue(ArrayItemsSourceProperty, value); }
}
public static readonly DependencyProperty myDescriptionProperty = DependencyProperty.Register("myDescription", typeof(string), typeof(myControl), new PropertyMetadata(null));
public static readonly DependencyProperty ArrayItemsSourceProperty = DependencyProperty.Register(nameof(ArrayItemsSource), typeof(string[]), typeof(myControl), new PropertyMetadata(null));
}
Related
In a WPF project (code below) I have a UserControl of type MyUserControl with a dependency property, called MyOrientation of type Orientation.
On the MainWindow I have 2 instances of MyUserControl, where via XAML I set the Orientation property on one to be Horizontal and the other instance to be Vertical.
I have made the MyOrientation property a DP as I want the ability to set it directly in XAML as in this example or using a binding.
My problem is that when I run the project both instances of the UserControl show up with the Orientation = Horizontal?
Could someone please tell me what I am doing wrong and how to fix it?
Many thanks in advance.
Here is the code:
MYUSERCONTROLVIEWMODEL:
public class MyUserControlViewModel : ViewModelBase
{
private Orientation _myOrientation;
public Orientation MyOrientation
{
get { return _myOrientation; }
set
{
if (_myOrientation == value)
return;
_myOrientation = value;
OnPropertyChanged();
}
}
}
MYUSERCONTROL.XAML
<UserControl x:Class="TestUserControlDPProblem.MyUserControl"
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:local="clr-namespace:TestUserControlDPProblem"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="root">
<Grid.DataContext>
<local:MyUserControlViewModel/>
</Grid.DataContext>
<StackPanel Orientation="{Binding MyOrientation}">
<TextBlock>Hello</TextBlock>
<TextBlock>There</TextBlock>
</StackPanel>
</Grid>
MYUSERCONTROL CODE BEHIND:
public partial class MyUserControl : UserControl
{
MyUserControlViewModel _vm;
public MyUserControl()
{
InitializeComponent();
_vm = root.DataContext as MyUserControlViewModel;
}
public static readonly DependencyProperty MyOrientationProperty = DependencyProperty.Register("MyOrientation", typeof(Orientation), typeof(MyUserControl), new FrameworkPropertyMetadata(Orientation.Vertical, new PropertyChangedCallback(OnMyOrientationChanged)));
private static void OnMyOrientationChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var myUserControl = o as MyUserControl;
myUserControl?.OnMyOrientationChanged((Orientation)e.OldValue, (Orientation)e.NewValue);
}
protected virtual void OnMyOrientationChanged(Orientation oldValue, Orientation newValue)
{
_vm.MyOrientation = newValue;
}
public Orientation MyOrientation
{
get
{
return (Orientation)GetValue(MyOrientationProperty);
}
set
{
SetValue(MyOrientationProperty, value);
}
}
}
MAINWINDOW.XAML
<Window x:Class="TestUserControlDPProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestUserControlDPProblem"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<local:MyUserControl Margin="10" MyOrientation="Horizontal"/>
<local:MyUserControl Margin="10" MyOrientation="Vertical"/>
</StackPanel>
</Grid>
The "internal" view model of the UserControl makes no sense and should not be there. You should instead bind directly to the dependency property by means of a RelativeSource or ElementName Binding:
<StackPanel Orientation="{Binding MyOrientation,
RelativeSource={RelativeSource AncestorType=UserControl}}">
You wouldn't even need the PropertyChangedCallback:
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty MyOrientationProperty =
DependencyProperty.Register(
nameof(MyOrientation), typeof(Orientation), typeof(MyUserControl),
new FrameworkPropertyMetadata(Orientation.Vertical));
public Orientation MyOrientation
{
get { return (Orientation)GetValue(MyOrientationProperty); }
set { SetValue(MyOrientationProperty, value); }
}
}
I have one dll which contains the wpf user control.
I have one wpf window in another wpf project which contains the above user control in that.
I have two public properties in wpf user control.
I want to set those properties from the wpf window in which wpf user control is added.
I have tried to do it using dependency property as follows :
TestUserControl.xaml :-
<UserControl x:Class="TestDependencyProperty.TestUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Label Content="test property" x:Name="lblTestProperty"/>
</Grid>
</UserControl>
TestUserControl.xaml.cs :-
using System.ComponentModel;
using System.Windows;
namespace TestDependencyProperty
{
/// <summary>
/// Interaction logic for TestUserControl.xaml
/// </summary>
public partial class TestUserControl
{
public TestUserControl()
{
InitializeComponent();
SetLabelText();
}
private void SetLabelText()
{
lblTestProperty.Content = TestProperty;
}
public static readonly DependencyProperty TestDependencyProperty =
DependencyProperty.Register("TestProperty",
typeof(string),
typeof(TestUserControl));
[Bindable(true)]
public string TestProperty
{
get
{
return (string)this.GetValue(TestDependencyProperty);
}
set
{
this.SetValue(TestDependencyProperty, value);
}
}
}
MainWindow.xaml :-
<Window x:Class="TestDependencyProperty.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:local="clr-namespace:TestDependencyProperty"
>
<Grid>
<local:TestUserControl x:Name="ucTest" TestProperty="HelloWorld"/>
</Grid>
</Window>
I am expecting a label with content "HelloWorld".
So can anybody tell me how to do it ?
Thanks.
User Control XAML:
<UserControl x:Class="TestDependencyProperty.TestUserControl"
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:local="clr-namespace:WpfApplication3"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Label Content="{Binding TestProperty}" x:Name="lblTestProperty"/>
</Grid>
</UserControl>
User Control Code:
public partial class TestUserControl : UserControl
{
public TestUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty TestDependencyProperty =
DependencyProperty.Register("TestProperty",
typeof(string),
typeof(TestUserControl));
[Bindable(true)]
public string TestProperty
{
get
{
return (string)this.GetValue(TestDependencyProperty);
}
set
{
this.SetValue(TestDependencyProperty, value);
}
}
}
You do not need SetLabelText();
Window Hosting User Control
<local:TestUserControl TestProperty="Test Text" x:Name="MyNewUserControl" />
in code behind if needed:
MyNewUserControl.TestProperty="New Value";
Dependency Properties have change notification built in to them so once a property is bound to them it will automatically get updated when the property does.
The problem is that the code below which should pass a UserControl to Modal (is also UserControl) doesn't work. It looks simple, I must be missing something small here.
Here's the code
XAML:
<UserControl x:Class="Test.UserControls.Modal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Grid>
....
<ContentControl Content="{Binding PrimaryElement, ElementName=self}" />
</Grid>
<UserControl x:Class="Test.UserControls.SimpleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Grid>
<Label>Test</Label>
</Grid>
</UserControl>
C#:
namespace Test.UserControls
{
public partial class Modal: UserControl
{
public static readonly DependencyProperty PrimaryElementPropery =
DependencyProperty.Register("PrimaryElement", typeof(UserControl), typeof(Modal), new FrameworkPropertyMetadata
{
BindsTwoWayByDefault = false
});
public UserControl PrimaryElement
{
get { return (UserControl)GetValue(PrimaryElementPropery); }
set { SetValue(PrimaryElementPropery, value); }
}
public Modal (UserControl userControl)
{
// userControl is SimpleUserControl
PrimaryElement = userControl;
InitializeComponent();
}
}
}
As a result, the modal is showing but there's no SimpleUserControl in it. Something wrong with the binding.
I have solved my problem, it was stupid. The width and height were missing in SimpleUserControl. The element was showing but had no width and height.
I have this User control:
I added this user control to my Winforms application (simple BusyIndicator)
UserControl x:Class="Stackoverflow.MyBusyIndicator"
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:xctk="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<xctk:BusyIndicator x:Name="busyIndicator" IsBusy="{Binding IsBusy}" />
</Grid>
</UserControl>
And all i want is define Method that i can access from c# code in order to stop this indicator.
I believe what you want to do this in the code behind?
public partial class MyBusyIndicator : UserControl
{
public void ToggleIndicator(bool isBusy)
{
// Just an example, in reality you will want to access the BusyIndicator object.
this.IsBusy = isBusy;
}
}
Your XAML code is fine, now just create a Dependency Property and call it "IsBusy", which you then can bind to in your UserControl XAML using a DataBinding (to visually indicate the IsBusy property state),
public partial class BusyIndicator : UserControl
{
public BusyIndicator()
{
InitializeComponent();
}
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register("IsBusy", typeof(bool), typeof(BusyIndicator),
new PropertyMetadata(false));
}
Try this :
<UserControl x:Class="Stackoverflow.MyBusyIndicator"
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:xctk="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<xctk:BusyIndicator x:Name="myBusyIndicator" IsBusy="True" />
</Grid>
</UserControl>
Code Bahind :
namespace Stackoverflow
{
using System;
using System.Windows;
using System.Windows.Controls;
public partial class MyBusyIndicator : UserControl
{
public MyBusyIndicator()
{
this.InitializeComponent();
}
public void ShowIndicator(bool isBusy)
{
this.myBusyIndicator.IsBusy = isBusy;
}
}
}
If you have to access it through code-behind first provide a name attribute to the BusyIndicator control through Xaml first:
<xctk:BusyIndicator IsBusy="True" x:Name="busyIndicator" />
In your code behind create a method as below:
void SetIndicator(bool isBusy)
{
this.busyIndicator.IsBusy = isBusy;
}
If you are using MVVM bind your control's IsBusyProperty IsBusy={Binding IsBusy}
<xctk:BusyIndicator IsBusy={Binding IsBusy} />
And in your viewmodel define IsBusy property and create method as below:
void SetIndicator(bool isBusy)
{
IsBusy = isBusy;
}
So next time you want to set is as True call SetIndicator(true) or if you want to set it as false call SetIndicator(false).
Here is my user control(MonthCal)'s code behind.
public partial class MonthCal : UserControl
{
public DayOfWeek StartDayOfWeek { get { return (DayOfWeek)GetValue(StartDayOfWeekProperty); } set { SetValue(StartDayOfWeekProperty, value); } }
public static readonly DependencyProperty StartDayOfWeekProperty = DependencyProperty.Register("StartDayOfWeek", typeof(DayOfWeek), typeof(MonthCellHeader), new UIPropertyMetadata(DayOfWeek.Sunday, StartDayOfWeek_PropertyChanged));
//...
}
and also, here is a xaml of the MonthCal.
<UserControl x:Class="GCDR.MonthCal"
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"
mc:Ignorable="d">
<!-- ... -->
</UserControl>
And so, How can I set the 'StartDayOfWeek' dependency property in xaml? as you guys know, the following code is impossible:
<UserControl ...
StartDayOfWeek="Sunday">
</UserControl>
Please give me a help.
You can not use the dependency property in markup of the UserControl but you can use it when you place instance of the user control somewhere like so:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<Grid>
<local:UserControl1 local:StartDayOfWeek="Friday" />
</Grid>
</Window>
With in your user control you can bind some other property to your dependency property like so:
<UserControl x:Class="WpfApplication1.UserControl1"
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:local="clr-namespace:WpfApplication1"
mc:Ignorable="d" >
<Grid>
<Label Content="{Binding RelativeSource={RelativeSource AncestorType=local:UserControl1},Path=StartDayOfWeek}" />
</Grid>
</UserControl>
Why you cannot set StartDayOfWeek is that UserControl in XAML does not have StartDayOfWeek dependency property, in other word UserControl type is not MonthCal type.
As, in XAML, UserControl is base class of UserControl1, you can define MonthCal inherited UserControl and then declare MonthCal in XAML.
XAML
<local:MonthCal x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="MainWindow"
Height="350" Width="525"
StartDayOfWeek="Monday">
<Grid></Grid>
</local:MonthCal>
Codebehinde
namespace WpfApplication1
{
public class MonthCal : Window
{
public DayOfWeek StartDayOfWeek { get { return (DayOfWeek)GetValue(StartDayOfWeekProperty); } set { SetValue(StartDayOfWeekProperty, value); } }
public static readonly DependencyProperty StartDayOfWeekProperty =
DependencyProperty.Register("StartDayOfWeek", typeof(DayOfWeek), typeof(MonthCal), new UIPropertyMetadata(DayOfWeek.Sunday, StartDayOfWeek_PropertyChanged));
private static void StartDayOfWeek_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
public partial class MainWindow : MonthCal
{
public MainWindow()
{
InitializeComponent();
}
}
}