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}"/>
Related
I am creating a Usercontrol which will contain a dropdown and on opening of dropdown, I want to have an Add button. Everything is working fine but validation is not working for Usercontrol controls.
Here is my xaml code:
<UserControl x:Class="Splendid.Inventory.Presentation.Controls.CustomComboBox"
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:Splendid.Inventory.Presentation.Controls"
xmlns:conv="clr-namespace:Splendid.Inventory.Presentation.Converters"
mc:Ignorable="d" x:Name="ucCombo" Validation.ErrorTemplate="{x:Null}"
d:DesignHeight="30" d:DesignWidth="100">
<UserControl.Resources>
<KeyBinding x:Key="addNewBinding" Key="N" Modifiers="Shift" Command="{Binding AddNewCommand}"></KeyBinding>
<conv:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></conv:BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<ComboBox x:Name="hdnCombo" MaxDropDownHeight="200" Visibility="Visible" Style="{StaticResource DialogCombobox}"
DisplayMemberPath="{Binding DisplayMemberPath, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
SelectedValuePath="{Binding SelectedValuePath, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
SelectedValue="{Binding SelectedValue,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"
ItemsSource="{Binding ItemSource, UpdateSourceTrigger=PropertyChanged}"
GotFocus="hdnCombo_GotFocus" LostFocus="hdnCombo_LostFocus">
</ComboBox>
<Canvas Visibility="{Binding IsDropDownOpen, ElementName=hdnCombo, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid Canvas.Top="200">
<Button HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="btnTesst"
MouseEnter="btnTesst_MouseEnter">Add New (Shift + N)</Button>
</Grid>
</Canvas>
</Grid>
</UserControl>
I tried usercontrol with string property binding and textbox and I am able to get errors.
Here is my xaml.cs code:
public partial class CustomComboBox : UserControl, INotifyPropertyChanged, IDataErrorInfo
{
public ICommand AddNewCommand { get; set; }
public CustomComboBox()
{
InitializeComponent();
//this.DataContext = this;
LayoutRoot.DataContext = this;
AddNewCommand = new DelegateCommand(OnAddNewCommand);
}
private void OnAddNewCommand()
{
MessageBox.Show("Add New form");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public IEnumerable ItemSource
{
get { return (IEnumerable)GetValue(ItemSourceProperty); }
set
{
SetValue(ItemSourceProperty, value);
}
}
// Using a DependencyProperty as the backing store for ListItemSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemSourceProperty =
DependencyProperty.Register("ItemSource", typeof(IEnumerable), typeof(CustomComboBox));
public string AddItemControl
{
get { return (string)GetValue(AddItemControlProperty); }
set { SetValue(AddItemControlProperty, value); }
}
// Using a DependencyProperty as the backing store for AddItemControl. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AddItemControlProperty =
DependencyProperty.Register("AddItemControl", typeof(string), typeof(CustomComboBox), new PropertyMetadata(string.Empty));
public string DisplayMemberPath
{
get { return (string)GetValue(DisplayMemberNameProperty); }
set { SetValue(DisplayMemberNameProperty, value); }
}
// Using a DependencyProperty as the backing store for DisplayMemberPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayMemberNameProperty =
DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(CustomComboBox), new PropertyMetadata(string.Empty));
public string SelectedValuePath
{
get { return (string)GetValue(SelectedValueNameProperty); }
set { SetValue(SelectedValueNameProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedValuePath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedValueNameProperty =
DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(CustomComboBox), new PropertyMetadata(string.Empty));
public SelectListModel SelectedItem
{
get { return (SelectListModel)GetValue(SelectedItemProperty); }
set
{
SetValue(SelectedItemProperty, value);
}
}
// Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(SelectListModel), typeof(CustomComboBox));
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(object), typeof(CustomComboBox));
public int ItemUnitId
{
get { return (int)GetValue(ItemUnitIdProperty); }
set { SetValue(ItemUnitIdProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemUnitIdProperty =
DependencyProperty.Register("ItemUnitId", typeof(string), typeof(CustomComboBox), new FrameworkPropertyMetadata(string.Empty,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
public string Error
{
get
{
return "Item type is required";
//throw new NotImplementedException();
}
}
public string this[string columnName]
{
get
{
// use a specific validation or ask for UserControl Validation Error
return Validation.GetHasError(this) ? Convert.ToString(Validation.GetErrors(this).FirstOrDefault().ErrorContent) : null;
}
}
private void btnTesst_MouseEnter(object sender, MouseEventArgs e)
{
OnAddNewCommand();
}
bool isFocused = false;
private void hdnCombo_GotFocus(object sender, RoutedEventArgs e)
{
isFocused = true;
}
private void hdnCombo_LostFocus(object sender, RoutedEventArgs e)
{
isFocused = false;
}
}
Is there any missing piece?
Resolved the issue by adding validation template to User control. Here are the changes made to UserControl:
<UserControl x:Class="Splendid.Inventory.Presentation.Controls.CustomComboBox"
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:Splendid.Inventory.Presentation.Controls"
xmlns:conv="clr-namespace:Splendid.Inventory.Presentation.Converters"
mc:Ignorable="d" x:Name="ucCombo"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}"
d:DesignHeight="30" d:DesignWidth="100">
<UserControl.Resources>
<KeyBinding x:Key="addNewBinding" Key="N" Modifiers="Shift" Command="{Binding AddNewCommand}"></KeyBinding>
<conv:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></conv:BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<ComboBox x:Name="hdnCombo" MaxDropDownHeight="200" Visibility="Visible" Style="{StaticResource DialogCombobox}"
DisplayMemberPath="{Binding DisplayMemberPath, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
SelectedValuePath="{Binding SelectedValuePath, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
SelectedValue="{Binding SelectedValue,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"
ItemsSource="{Binding ItemSource, UpdateSourceTrigger=PropertyChanged}"
GotFocus="hdnCombo_GotFocus" LostFocus="hdnCombo_LostFocus">
</ComboBox>
<Canvas Visibility="{Binding IsDropDownOpen, ElementName=hdnCombo, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid Canvas.Top="200">
<Button HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="btnTesst"
MouseEnter="btnTesst_MouseEnter">Add New (Shift + N)</Button>
</Grid>
</Canvas>
</Grid>
</UserControl>
I have tried to google this but I am not super clear on the suggestions I see people making.
I have 3 buttons within a user control that are exactly the same except for automation id and text. Instead of duplicating this code across all buttons, I would like to create a common control or control template that I can use. My problem is these buttons have nested controls whose text I need to change but I'm not sure how to do this.
<UserControl>
<Grid>
<StackPanel x:Name="myStackPanel" Grid.Row="1" Padding="0">
<Button AutomationProperties.AutomationId="CallButton" x:Name="CallButton" Style="{StaticResource ButtonStyle}">
<RelativePanel>
<SymbolIcon AutomationProperties.AutomationId="CallIcon" x:Name="CallIcon" Symbol="Phone" Style="{StaticResource SymbolStyle}" />
<StackPanel>
<TextBlock AutomationProperties.AutomationId="CallLabel" Text="Call" Style="{StaticResource LabelStyle}"/>
<TextBlock AutomationProperties.AutomationId="CallText" Text="123-456-7890" Style="{StaticResource ContentStyle}"/>
</StackPanel>
</RelativePanel>
</Button>
<Button AutomationProperties.AutomationId="EmailButton" x:Name="EmailButton" Style="{StaticResource ButtonStyle}">
<RelativePanel>
<SymbolIcon AutomationProperties.AutomationId="EmailIcon" x:Name="EmailIcon" Symbol="Mail" Style="{StaticResource SymbolStyle}"/>
<StackPanel >
<TextBlock AutomationProperties.AutomationId="EmailLabel" Text="Email" Style="{StaticResource LabelStyle}"/>
<TextBlock AutomationProperties.AutomationId="EmailText" Text="meetme#email.com" Style="{StaticResource ContentStyle}"/>
</StackPanel>
</RelativePanel>
</Button>
<Button AutomationProperties.AutomationId="WebsiteButton" x:Name="WebsiteButton" Style="{StaticResource ButtonStyle}">
<RelativePanel>
<SymbolIcon AutomationProperties.AutomationId="WebsiteIcon" x:Name="WebsiteIcon" Symbol="Link" Style="{StaticResource SymbolStyle}"/>
<StackPanel >
<TextBlock AutomationProperties.AutomationId="WebsiteLabel" Text="Website" Style="{StaticResource LabelStyle}"/>
<TextBlock AutomationProperties.AutomationId="WebsiteText" Text="http://meetme.com" Style="{StaticResource ContentStyle}"/>
</StackPanel>
</RelativePanel>
</Button>
</StackPanel>
</Grid>
</Grid>
As you can see, the code for all 3 buttons is same. All I want to do is create a control where I can set the automation ids and text properties of the nested controls.
Thanks!
You can create a button based on a UserControl (add a new UserControl). It will allow you to enjoy all the default button's properties, events and states (OnClick, Command, etc.) and to add your own properties, template and behavior.
Using dependency properties instead of simple properties is strongly advised if you want to use bindings or animations on them.
C#:
public partial class CustomButton : Button
{
#region IconAutomationId
public string IconAutomationId
{
get { return (string)GetValue(IconAutomationIdProperty); }
set { SetValue(IconAutomationIdProperty, value); }
}
public static readonly DependencyProperty IconAutomationIdProperty =
DependencyProperty.Register("IconAutomationId", typeof(string), typeof(CustomButton), new PropertyMetadata(null));
#endregion
#region LabelAutomationId
public string LabelAutomationId
{
get { return (string)GetValue(LabelAutomationIdProperty); }
set { SetValue(LabelAutomationIdProperty, value); }
}
public static readonly DependencyProperty LabelAutomationIdProperty =
DependencyProperty.Register("LabelAutomationId", typeof(string), typeof(CustomButton), new PropertyMetadata(null));
#endregion
#region TextAutomationId
public string TextAutomationId
{
get { return (string)GetValue(TextAutomationIdProperty); }
set { SetValue(TextAutomationIdProperty, value); }
}
public static readonly DependencyProperty TextAutomationIdProperty =
DependencyProperty.Register("TextAutomationId", typeof(string), typeof(CustomButton), new PropertyMetadata(null));
#endregion
#region Symbol
public object Symbol
{
get { return (object)GetValue(SymbolProperty); }
set { SetValue(SymbolProperty, value); }
}
public static readonly DependencyProperty SymbolProperty =
DependencyProperty.Register("Symbol", typeof(object), typeof(CustomButton), new PropertyMetadata(null));
#endregion
#region Label
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(CustomButton), new PropertyMetadata(null));
#endregion
#region Text
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(CustomButton), new PropertyMetadata(null));
#endregion
public CustomButton()
{
InitializeComponent();
}
}
XAML:
<Button x:Class="WpfApplication1.CustomButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="customButton">
<RelativePanel>
<SymbolIcon AutomationProperties.AutomationId="{Binding IconAutomationId, ElementName=customButton}"
Symbol="{Binding Symbol, ElementName=customButton}"
Style="{StaticResource SymbolStyle}"/>
<StackPanel >
<TextBlock AutomationProperties.AutomationId="{Binding LabelAutomationId, ElementName=customButton}"
Text="{Binding Label, ElementName=customButton}"
Style="{StaticResource LabelStyle}"/>
<TextBlock AutomationProperties.AutomationId="{Binding TextAutomationId, ElementName=customButton}"
Text="{Binding Text, ElementName=customButton}"
Style="{StaticResource ContentStyle}"/>
</StackPanel>
</RelativePanel>
</Button>
Use:
<local:CustomButton AutomationProperties.AutomationId="CallButton"
x:Name="CallButton"
Style="{StaticResource ButtonStyle}"
IconAutomationId="CallIcon"
LabelAutomationId="CallLabel"
TextAutomationId="CallText"
Symbol="Phone"
Label="Call"
Text="123-456-7890"/>
It may be more work than you feel like doing but it sounds like you're going to want to create a UserControl. It should inherit from Button in the code-behind:
public partial class MyButton: Button
In the XAML you will include essentially the guts of what you have in each of the buttons now.
The tedious part is that you will then (in the code-behind) need to create a DependencyProperty for each of the "properties" you will want to set in this control: for example, one for the CallIconAutomationId, one for the CallLabelAutomationId, one for the CallLabelText, etc. You will then bind each of these properties in the XAML to the dependency property. These properties become the data that you will set on each individual MyButton (your new UserControl).
Then, in the container that is hosting these controls (which appears to be another UserControl in your example above) you would set these custom properties on each of your new MyButton controls, which will look something like this:
<myNamespace:MyButton EmailIconAutomationId="EmailIcon" LabelAutomationId="EmailLabel" />
etc.
Basically, you're creating a new control (a UserControl) based on the Button control (which gives you most of your functionality) and adding new custom properties directly to that new control (which work just like all the other control properties you're accustomed to).
In the code below, I have a UserControl that contains an ellipse and a textblock. I'd like to create a reusable control that I can bind to that allows me to set the text based on a string, and changes the fill color between Red/Green based on a boolean.
I can do this now by digging deep into the markup and using some complex binding, but I want to reuse this control in a list and it seemed easier to create a control for the purpose. However, I am not sure where to go next and if I should be creating dependency-properties that tie to the values for Fill and Text, or what.
<UserControl
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"
mc:Ignorable="d"
x:Class="Herp.Derp.View.DeliveryStatusIndicator"
x:Name="UserControl"
d:DesignWidth="91" d:DesignHeight="35">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<Ellipse Width="35" Height="35" Fill="Green">
<Ellipse.OpacityMask>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_location_circle}"/>
</Ellipse.OpacityMask>
</Ellipse>
<TextBlock Style="{StaticResource Heading2}"
VerticalAlignment="Center" Margin="3,0,0,0">
<Run Text="FRONT"/>
</TextBlock>
</StackPanel>
</Grid>
</UserControl>
here how you can achieve this
UserControl
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty FrontTextProperty = DependencyProperty.Register( "FrontText", typeof(string),typeof(UserControl1), new FrameworkPropertyMetadata(string.Empty));
public string FrontText
{
get { return (string)GetValue(FrontTextProperty); }
set {
SetValue(FrontTextProperty, value);
frontBlock.Text = value;
}
}
public static readonly DependencyProperty EllipseStateProperty = DependencyProperty.Register("EllipseState", typeof(bool), typeof(UserControl1), new FrameworkPropertyMetadata(false));
public bool EllipseState
{
get { return (bool)GetValue(EllipseStateProperty); }
set
{
SetValue(EllipseStateProperty, value);
if (value)
{
ellipse.Fill = new SolidColorBrush( Colors.Green);
}
else
{
ellipse.Fill = new SolidColorBrush(Colors.Red);
}
}
}
}
MainWindow
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 EllipseState="{Binding yourProperty }"/>
<CheckBox Content="CheckBox" HorizontalAlignment="Left" Margin="207,94,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
Yes, to create properties that "parent" XAML can assign bindings to, you need to create aDependencyProperty for each field that you want to bind to.
You would then bind your user control xaml to the backing property for the DP.
Here's what I got for a working solution:
public partial class DeliveryStatusIndicator : UserControl
{
public DeliveryStatusIndicator()
{
InitializeComponent();
}
public static readonly DependencyProperty DeliveryDescriptionProperty = DependencyProperty.Register("DeliveryDescription", typeof(string), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata("Default", DescriptionChangedEventHandler));
private static void DescriptionChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Desc.Text = (string)e.NewValue;
}
public string DeliveryDescription
{
get { return (string)GetValue(DeliveryDescriptionProperty); }
set
{
SetValue(DeliveryDescriptionProperty, value);
}
}
public static readonly DependencyProperty DeliveryStatusProperty = DependencyProperty.Register("DeliveryStatus", typeof(bool), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata(false, StatusChangedEventHandler));
private static void StatusChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Indicator.Fill = (bool)e.NewValue ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
}
public bool DeliveryStatus
{
get { return (bool)GetValue(DeliveryStatusProperty); }
set
{
SetValue(DeliveryStatusProperty, value);
}
}
}
I want to create a custom control(or user control) which has both a textblock and a textbox as in the fig below:
Both the textblock and Textbox text properties are to be bound to the database fields and be able to apply styling etc. Which is the best approach for the same?
I have come up with a solution as below:
XAML for user control:
<UserControl x:Class="TestDependency.TextBlox"
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"
x:Name="abc"
DataContext="{Binding RelativeSource={RelativeSource Self}}" >
<Grid Width="170">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="0,1,0,0"
Text="{Binding Path=Caption}"
VerticalAlignment="Top" Width="52" />
<TextBox Height="25" HorizontalAlignment="Left" Margin="53,-2,0,0"
x:Name="TextBox1"
Text="{Binding Path=Value}"
VerticalAlignment="Top" Width="120" />
</Grid>
Code behind for Usercontrol:
public partial class TextBlox : UserControl
{
public TextBlox()
{
InitializeComponent();
}
public static DependencyProperty CaptionProperty = DependencyProperty.Register("Caption", typeof(string), typeof(TextBlox));
public static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(TextBlox));
public string Caption
{
get
{
return (string)GetValue(CaptionProperty);
}
set
{
SetValue(CaptionProperty, value);
}
}
public string Value
{
get
{
return (string)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
static void ValueChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
TextBlox inst = (TextBlox)property;
inst.TextBox1.Text = (string)args.NewValue;
}
}
XAML for actual form where Usercontrol is used:
<Grid x:Name="grdmain">
<my:TextBlox Caption="{Binding XValue, Mode=TwoWay}" Value="{Binding WindowName, Mode=TwoWay}"
HorizontalAlignment="Left" Margin="246,197,0,0" x:Name="textBlox1" VerticalAlignment="Top" />
</Grid>
Code behind:
public partial class MainWindow : Window
{
DataEntities dt = new DataEntities();
CoOrdinate oCord;
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
oCord = dt.CoOrdinates.First();
grdmain.DataContext = oCord;
}
}
Still the binding doesnt work. But the code :
textBlox1.Caption = "test";
is working fine. where am I wrong?
Create an user control with a textblock and textbox and bind the values to that control.
<YourUserControl TextBlockText="{Binding TextBlockText}" TextBoxText="{Binding TextBoxText, Mode=TwoWay}"/>
I have a UserControl which acts as a wrapper for a ContentControl, which is simply a title to the ContentControl.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Background="Green" Grid.Row="0">
<TextBlock Text="{Binding Header}" Style="{StaticResource HeaderStyle}" Margin="12, 10, 0, 10" />
</Grid>
<ContentControl HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Content="{Binding Body}" Grid.Row="1"/>
</Grid>
And here's where I try to use the control:
<gbl:ListHeader Grid.Row="1" Visibility="{Binding HasMovies, Converter={StaticResource VisibilityConverter}}" Header="{Binding Path=LocalizedResources.movie_list_header, Source={StaticResource LocalizedStrings}}" >
<gbl:ListHeader.Body>
<ListBox SelectionChanged="ListBoxContainerSelectionChanged" ItemsSource="{Binding Movies}" ItemContainerStyle="{StaticResource HeaderListBoxItemStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<gbl:MovieItemControl Header="{Binding MovieTitle}" Description="{Binding FormattedDescription}" Detail="{Binding FormattedDetail}" Opacity="{Binding IsSuppressed, Converter={StaticResource DimIfTrueConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</gbl:ListHeader.Body>
The DataBinding to the list happens, however nothing displays in the control. I'm guessing that it's still there, but too small to see (undefined h/w).
Is there something that I'm doing wrong? The header shows fine, so the control appears to be working somewhat.
Edit:
Here's the code-behind for ListHeader:
public partial class ListHeader : UserControl
{
private readonly ListHeaderData _data = new ListHeaderData();
public ListHeader()
{
InitializeComponent();
DataContext = _data;
}
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(ListHeader), new PropertyMetadata("",HeaderPropertyChanged) );
private static void HeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var lh = d as ListHeader;
if (lh != null)
lh._data.Header = e.NewValue as string;
}
public object Body
{
get { return GetValue(BodyProperty); }
set { SetValue(BodyProperty, value); }
}
// Using a DependencyProperty as the backing store for Body. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BodyProperty =
DependencyProperty.Register("Body", typeof(object), typeof(ListHeader), new PropertyMetadata(null, BodyPropertyChanged));
private static void BodyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var lh = d as ListHeader;
if (lh != null)
lh._data.Body = e.NewValue;
}
}
public class ListHeaderData : ViewModelBase
{
public ListHeaderData()
{
if (IsInDesignMode)
{
Header = "Custom Header Goes Here";
Body = new Grid() { Background = new SolidColorBrush(Colors.Yellow) };
}
}
private string _header;
public string Header
{
get { return _header; }
set { _header = value; RaisePropertyChanged("Header"); }
}
private object _body;
public object Body
{
get { return _body; }
set { _body = value; RaisePropertyChanged("Body");}
}
}
In addition to what i said in my comment you appear to bind to your DataContext in the UserControl declaration which is a Bad Thing and the problem of all this.
You appear to want to bind to the properties of the UserControl but you bind directly to the properties of the DataContext which is your ViewModel, hence setting the Body property on an instance in XAML does nothing as the property is sidestepped by the internal binding.
UserControls should for all i know do bindings like this:
<UserControl Name="control" ...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Background="Green" Grid.Row="0">
<TextBlock Text="{Binding Header, ElementName=control}" Style="{StaticResource HeaderStyle}" Margin="12, 10, 0, 10" />
</Grid>
<ContentControl HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Content="{Binding Body, ElementName=control}" Grid.Row="1"/>
</Grid>
Get rid of those dependency property changed callbacks and change the property code in ViewModels to this format to make sure it changed:
private int _MyProperty = 0;
public int MyProperty
{
get { return _MyProperty; }
set
{
if (_MyProperty != value)
{
_MyProperty = value;
OnPropertyChanged("MyProperty");
}
}
}