I am trying to pass parameter in my usercontrol with DependencyProperty.
This is my main view :
<control:HeaderControl TestText="test" DataContext="{Binding HeaderViewModel, Source={StaticResource Locator}}" />
This is my code behind of my userControl :
public partial class Connexion : INotifyPropertyChanged
{
public Connexion()
{
InitializeComponent();
}
public static readonly DependencyProperty TestTitleProperty =
DependencyProperty.Register(
"TestText",
typeof(string),
typeof(Connexion),
new PropertyMetadata(default(string), null));
public string TestText
{
get { return (string)GetValue(TestTitleProperty); }
set { SetValue(TestTitleProperty, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
//this.DataContext = new ConnexionViewModel();
}
And then, there is no data in my userControl..., my TextBlock is empty :
<TextBlock x:Name="headerTitle" Text="{Binding TestText}" FontWeight="Bold" FontSize="24" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center"/>
Anyone have an idea?
Thanks.
Related
I have an UserControl that have Object Propdp:
public partial class HeaderControl : UserControl
{
public HeaderControl()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty ControlContentProperty =
DependencyProperty.Register(nameof(ControlContent), typeof(object),
typeof(HeaderControl), new PropertyMetadata(null));
public object ControlContent
{
get { return (object)GetValue(ControlContentProperty); }
set { SetValue(ControlContentProperty, value); }
}
}
In Xaml file:
<Grid Grid.Row="1">
<ContentControl Content="{Binding ControlContent, ElementName=root}"/>
</Grid>
if i use it without give a Name to the Button inside it works:
<ctrl:HeaderContorl >
<ctrl:HeaderContorl.ControlContent>
<Button Content="Hallooo" FontSize="40"
Foreground="Black" Height="100"/>
</ctrl:HeaderContorl.ControlContent>
</ctrl:HeaderContorl>
But if i name the Button inside it not works:
<ctrl:HeaderContorl >
<ctrl:HeaderContorl.ControlContent>
<Button x:Name="Btn" Content="Hallooo" FontSize="40"
Foreground="Black" Height="100"/>
</ctrl:HeaderContorl.ControlContent>
</ctrl:HeaderContorl>
Can someone please tell me why I can't name the button inside?
I am trying to create a multi-select Combobox Custom control, This custom control should expose a dependency property called DropDownDataSource through which the user of the control can decide what day should bound to ComboBox. My code looks like this:
MainPage.Xaml
<Grid>
<local:CustomComboBox x:Name="customcb" DropDownDataSource="{x:Bind DropDownDataSource, Mode=OneWay}" Loaded="CustomControl_Loaded"> </local:CustomComboBox>
</Grid>
MainPage.Xaml.cs
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private ObservableCollection<Item> _dropDownDataSource;
public ObservableCollection<Item> DropDownDataSource
{
get => _dropDownDataSource;
set
{
_dropDownDataSource = value;
OnPropertyChanged();
}
}
public MainPage()
{
this.InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private void CustomControl_Loaded(object sender, RoutedEventArgs e)
{
var Items = new ObservableCollection<Item>(Enumerable.Range(1, 10)
.Select(x => new Item
{
Text = string.Format("Item {0}", x),
IsChecked = x == 40 ? true : false
}));
DropDownDataSource = Items;
}
}
Models
public class Item : BindableBase
{
public string Text { get; set; }
bool _IsChecked = default;
public bool IsChecked { get { return _IsChecked; } set { SetProperty(ref _IsChecked, value); } }
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T storage, T value,
[System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (!object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
CustomUserControl XAML
<Grid x:Name="GrdMainContainer">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox Width="200" FontSize="24" Text="{Binding Header, Mode=TwoWay}"
IsReadOnly="True" TextWrapping="Wrap" MaxHeight="200" />
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="200" Width="200" Background="White">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}"
FontSize="24"
Foreground="Black"
IsChecked="{Binding IsChecked, Mode=TwoWay}"
IsThreeState="False" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Grid>
CustomUserControl Cs file
public sealed partial class CustomComboBox : UserControl
{
public CustomComboBox()
{
this.InitializeComponent();
}
public ObservableCollection<Item> DropDownDataSource
{
get { return (ObservableCollection<Item>)GetValue(DropDownDataSourceProperty); }
set { SetValue(DropDownDataSourceProperty, value); }
}
public static readonly DependencyProperty DropDownDataSourceProperty =
DependencyProperty.Register("DropDownDataSource", typeof(ObservableCollection<Item>), typeof(CustomComboBox), new PropertyMetadata("", HasDropDownItemUpdated));
private static void HasDropDownItemUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CustomComboBox ucrcntrl)
{
var grd = UIElementExtensions.FindControl<Grid>(ucrcntrl, "GrdMainContainer");
grd.DataContext = ucrcntrl.DropDownDataSource as ObservableCollection<Item>;
}
}
}
All looks good to me, but for some reason, Dropdown is coming empty. Instead of the dependency property, If I assign a view model directly to the Control it works fine. But in my condition, it is mandatory that I have properties like DataSource,SelectedIndex, etc on the user control for the end-user to use. Can anyone point out what is going wrong here?
Here, I have attached a copy of my complete code.
I downloaded your sample code, the problem should be in the binding.
<ItemsControl ItemsSource="{Binding Items}">
This way of writing is not recommended. In the ObservableCollection, Items is a protected property and is not suitable as a binding property.
You can try to bind dependency property directly in ItemsControl:
<ItemsControl ItemsSource="{x:Bind DropDownDataSource,Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:Item">
<CheckBox IsChecked="{x:Bind IsChecked, Mode=TwoWay}"
IsThreeState="False" >
<TextBlock Text="{x:Bind Text}" Foreground="Black" FontSize="24"/>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In addition, you may have noticed that I modified the style of CheckBox and rewritten the content to TextBlock, because in the default style of CheckBox, Foreground is not bound to the internal ContentPresenter.
Thanks.
I've uploaded my sample project here: https://www.file-upload.net/download-13252079/WpfApp1.zip.html
It is a WPF window with several of the same usercontrols in it. The UserControl has a dependency property named "Text" which is bound to a MainWindowViewModel's property and successfully shows up in the UserControl's TextBlock.
However if I double-click the UserControl and want it to give the value of its dependency property, the value is null. Why is this?
Thanks a lot for your help!
Edit: sorry, here is some source code:
The UserControl's XAML:
<UserControl x:Class="WpfApp1.UserControl1"
...
x:Name="UC1">
<StackPanel Orientation="Vertical">
<TextBlock Margin="5" Text="Test" FontSize="20"></TextBlock>
<TextBlock Margin="5" Text="{Binding ElementName=UC1, Path=Text}" FontSize="20"></TextBlock>
</StackPanel>
</UserControl>
The UserControl's code:
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public UserControl1()
{
InitializeComponent();
}
string text;
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(UserControl1));
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The main window's XAML:
<Window x:Class="WpfApp1.MainWindow"
...>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<StackPanel>
<local:UserControl1 Text="{Binding Values[0]}"
MouseDoubleClick="UserControl1_MouseDoubleClick">
</local:UserControl1>
<local:UserControl1 Text="{Binding Values[1]}"
MouseDoubleClick="UserControl1_MouseDoubleClick">
</local:UserControl1>
<local:UserControl1 Text="{Binding Values[2]}"
MouseDoubleClick="UserControl1_MouseDoubleClick">
</local:UserControl1>
<local:UserControl1 Text="{Binding Values[3]}"
MouseDoubleClick="UserControl1_MouseDoubleClick">
</local:UserControl1>
</StackPanel>
</Window>
The main window's code behind:
private void UserControl1_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender is UserControl1)
{
// why is (sender as UserControl1).Text null?
MessageBox.Show("Text is: " + (sender as UserControl1).Text);
}
}
The main window's view model:
class MainWindowViewModel : BindableBase
{
public MainWindowViewModel()
{
Values = new ObservableCollection<string>();
Values.Add("first string");
Values.Add("second string");
Values.Add("third string");
Values.Add("fourth string");
}
#region Values
private ObservableCollection<string> values;
public ObservableCollection<string> Values
{
get { return values; }
set { SetProperty(ref values, value); }
}
#endregion
}
Here is how your UserControl's code behind should look like. You do not need to implement INotifyPropertyChanged.
See Custom Dependency Properties for all the details. Specifically, you must call GetValue and SetValue (and nothing else) from the getter and setter of the Text property wrapper.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text), typeof(string), typeof(UserControl1));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
For a Binding to the UserControl's Text property in its own XAML you could use RelativeSource instead of ElementName to save a useless generated class member:
<UserControl x:Class="WpfApp1.UserControl1" ...>
...
<TextBlock Text="{Binding Text,
RelativeSource={RelativeSource AncestorType=UserControl}}" .../>
...
</UserControl>
I am new to object binding and I don' succeed to make it work.
I have a xaml window with the following textbox:
<Grid x:Name="gr_main" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="180,65,0,0" DataContext="{Binding currentproj}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="2" x:Name="txt_localdir" Height="25" TextWrapping="Wrap" Width="247" IsEnabled="False" Text="{Binding Path=Localdir, UpdateSourceTrigger=PropertyChanged}"/>
In the cs code of the main window, I define an instance of my Project class, called currentproj, as follows:
public partial class MainWindow : Window{
Project currentproj;
public MainWindow()
{
currentproj = new Project();
InitializeComponent();
}}
The project class (defined in a Project.cs file) is as follows:
public partial class Project : Component, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _localdir;
public string Localdir
{
get { return _localdir; }
set
{
if (value != _localdir)
{
_localdir = value;
NotifyPropertyChanged("Localdir");
}
}
}
public Project()
{
InitializeComponent();
}
public Project(IContainer container)
{
container.Add(this);
InitializeComponent();
}}
However, even if I am binding the textbox.text attribute to the Localdir path of the currentproj object, the textbox is never updated. I see the PropertyChanged event is alwais null when I set the value of Localdir, but I don't understand why.
Data binding works on the DataContext. The Grid's DataContext is not set correctly, this should be removed.
so the Grid definition should be:
<Grid x:Name="gr_main" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="180,65,0,0">
Setting the Window DataContext to currentProj is done by:
public partial class MainWindow : Window{
Project currentproj;
public MainWindow()
{
currentproj = new Project();
DataContext = currentproj;
InitializeComponent();
}}
I'm trying to create a reusable UserControl in WPF that has a Label and a TextBox. I want to add properties to my UserControl to bubble up the Text fields of both child controls up to the parent for easy binding. I read that I need to a little bit of hocus pocus by adding owners to DependencyProperties. Here is my code now. It seems close but not quite right. Any ideas?
Here is the Xaml:
<UserControl x:Class="MAAD.AircraftExit.Visual.LabelTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="20" Width="300">
<DockPanel>
<TextBlock Text="{Binding Path=Label, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" DockPanel.Dock="Left" TextAlignment="Right" Width="122" />
<TextBlock Text=": " DockPanel.Dock="Left"/>
<TextBox Text="{Binding Path=Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
</DockPanel>
</UserControl>
And the code behind:
public partial class LabelTextBox : UserControl
{
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(LabelTextBox));
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(LabelTextBox));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(LabelTextBox.TextProperty, value); }
}
public LabelTextBox()
{
InitializeComponent();
ClearValue(HeightProperty);
ClearValue(WidthProperty);
}
}
Edit: Here is the final working code. I switched over to relative source binding.
Binding is really the way to go:
XAML:
<UserControl x:Class="testapp.LabelTextBox "
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" x:Name="This">
<DockPanel>
<TextBlock DockPanel.Dock="Left" TextAlignment="Right" Width="70" Name="label" Text="{Binding Label, ElementName=This}" />
<TextBlock Text=": " DockPanel.Dock="Left" />
<TextBox Name="textBox" Text="{Binding Text, ElementName=This}" />
</DockPanel>
Code Behind:
public partial class LabelTextBox : UserControl
{
public LabelTextBox()
{
InitializeComponent();
Label = "Label";
Text = "Text";
}
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(LabelTextBox), new FrameworkPropertyMetadata(LabelPropertyChangedCallback));
private static void LabelPropertyChangedCallback(DependencyObject controlInstance, DependencyPropertyChangedEventArgs args)
{
}
public string Label
{
get { return (string) GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(LabelTextBox), new FrameworkPropertyMetadata(TextPropertyChangedCallback));
private static void TextPropertyChangedCallback(DependencyObject controlInstance, DependencyPropertyChangedEventArgs args)
{
}
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(LabelTextBox.TextProperty, value); }
}
}
I haven't looked into exactly why your implementation isn't working, but I don't really understand why you're doing it that way. Why not just define the dependency properties you need on the UserControl and then bind to them?
public static readonly DependencyProperty LabelTextProperty = ...;
And then in your XAML:
<Label Content="{Binding LabelText}"/>