Binding to custom control inside DataTemplate for ItemsControl
The problem is that you explicitly set the DataContext
of your UserControl to itself:
DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}
Remove that assignment and write the ItemName
binding like this:
<TextBlock Text="{Binding ItemName,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
or like this
<TextBlock Text="{Binding ItemName, ElementName=ItemRowControl}"/>
wpf customControl binding itemscontrol DataTemplate button
First the viewmodel with the ObservableCollection and the Command that will execute the X:
private ObservableCollection<string> items = new ObservableCollection<string>() { "One", "Two", "Three" };
public ObservableCollection<String> Items
{
get
{
return items;
}
set
{
items = value;
NotifyPropertyChanged();
}
}
public Command<String> DeleteItem
{
get
{
return new Command<string>((item) =>
{
if (items.Contains(item))
{
items.Remove(item);
}
NotifyPropertyChanged("Items");
});
}
}
Now the XAML:
Resources, where it is binded to the 'parent' datacontext, this is a easy way to know where the binding is going:
<Page.Resources>
<DataTemplate x:Key="DefaultSelectedItemsTemplate" >
<Border x:Name="selectedItemBorder" BorderBrush="Gray" BorderThickness="1" CornerRadius="5" Margin="5,1,1,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="15"/>
</Grid.ColumnDefinitions>
<!--<TextBlock Grid.Column="0" Text="{Binding RelativeSource={RelativeSource Self}, Path=Ime}" Margin="5,0,3,0"></TextBlock>-->
<TextBlock Grid.Column="0" Text="{Binding}" Margin="5,0,3,0"></TextBlock>
<Button x:Name="PART_selectedItemButton" BorderThickness="0" Grid.Column="1" Command="{Binding DataContext.DeleteItem, ElementName=ItemsControlInstance}" CommandParameter="{Binding}">X</Button>
</Grid>
</Border>
</DataTemplate>
</Page.Resources>
And finally the itemscontrol:
<ItemsControl x:Name="ItemsControlInstance" ItemTemplate="{StaticResource DefaultSelectedItemsTemplate}" ItemsSource="{Binding Items}"/>
WPF Custom Control inside ItemsControl
Your minimal custom TextBlock should at least also override the MeasureOverride
method to return its size. Otherwise the control has zero width and height, and all items in the ItemsControl are drawn on top of each other.
The Text
property should therefore be registered with the FrameworkPropertyMetadataOptions.AffectsMeasure
flag.
public class MyTextBlock : FrameworkElement
{
private FormattedText formattedText;
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text", typeof(string), typeof(MyTextBlock),
new FrameworkPropertyMetadata(
string.Empty,
FrameworkPropertyMetadataOptions.AffectsMeasure,
(o, e) => ((MyTextBlock)o).TextPropertyChanged((string)e.NewValue)));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
private void TextPropertyChanged(string text)
{
var typeface = new Typeface(
new FontFamily("Times New Roman"),
FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
formattedText = new FormattedText(
text, CultureInfo.CurrentCulture,
FlowDirection.LeftToRight, typeface, 15, Brushes.Black);
}
protected override Size MeasureOverride(Size availableSize)
{
return formattedText != null
? new Size(formattedText.Width, formattedText.Height)
: new Size();
}
protected override void OnRender(DrawingContext drawingContext)
{
if (formattedText != null)
{
drawingContext.DrawText(formattedText, new Point());
}
}
}
custom user control binding on itemspaneltemplate
Found what was causing the strange conflicting behavior.
I was setting the property to a certain value in the normal ctor
public CustomControl()
{
Mode = Modes.Default;
}
This was appearently causing a conflict when using the control as an Itemspaneltemplate. Removing this made the binding work as expected.
I guess the difference in behavior has something to do with the calls to the constructor at different times ?
Binding viewmodel within ItemsControl to custom user control
Remove Message
property from
<local:MessageControl Message="{Binding Path=., Mode=TwoWay}" />
It is not needed at all.
Remove DataContext
from MessageControl
which you have already done now.
Everything remaining same, now it will work.
The DataTemplate
will get it's DataContext
automatically as MessageViewModel
.
Binding inner ContentControl control to parent ItemsControl item
There are multiple controls derived from ContentControl
in the control hierarchy:
ContentControl
(yours)materialDesign:ColorZone
materialDesign:PopupBox
materialDesign:Card
(not shown)
That is why your binding does not work. It will return the data context of the first control while traversing the parent controls. This control is materialDesign:Card
and its data context Eeprom
.
You can make your binding work, if you specify which ContentControl
in the hierarchy you want. This is defined by the AncestorLevel
. Your target ContentControl
is the fourth in the parent hierarchy, so specify 4
.
<Button Tag="{Binding RelativeSource={RelativeSource AncestorType=ContentControl, AncestorLevel=4}, Path=DataContext}" Click="Button_Click"/>
An alternative to a RelativeSource
binding, that works here, is setting an x:Name
on your target ContentControl
and referring to it in the binding with ElementName
.
<ItemsControl ItemsSource="{Binding Parent}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Eeprom is an object within "Board" item-->
<ContentControl x:Name="MyContentControl" Content="{Binding Text}">
<ContentControl.ContentTemplate>
<DataTemplate>
<materialDesign:ColorZone>
<materialDesign:PopupBox>
<StackPanel>
<TextBox Text="{Binding Mode=OneWay}"/>
<Button Tag="{Binding DataContext, ElementName=MyContentControl}" Click="Button_Click"/>
</StackPanel>
</materialDesign:PopupBox>
</materialDesign:ColorZone>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Handle events from DataTemplate in custom control
I recommend you to fire a Command
on MouseDoubleClick
event.
To connect a MouseDoubleClick
event to a Command
and pass ClientId
to it, you can use somethings like this:
<ScrollViewer x:Name="srcContacts" Grid.Row="0" Margin="0,0,0,0" VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=People, RelativeSource={RelativeSource AncestorType={x:Type chat:ChatClient}}}" x:Name="Contacts">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl>
<!-- ــــInputBinding For Mouse LeftDoubleClickــــ -->
<ContentControl.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl},Path=DataContext.ClientDoubleClickCommand}"
CommandParameter="{Binding ClientId}"/>
<ContentControl.InputBindings>
<!-- ــــــــــــــــــــــــــــــــــــــــ -->
<Border Style="{StaticResource IsMouseOver}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" VerticalAlignment="Top" Height="35" Width="35" Margin="5,0,0,0" BorderBrush="{StaticResource ChatClientPresenceOnlineBrush}" BorderThickness="2" CornerRadius="15">
<Image Height="30" Width="30" Source="Cleo.Windows.Ui.Chat;component/Resources/noavatar.png" StretchDirection="Both" Stretch="Fill">
<Image.Clip>
<EllipseGeometry Center="15,15" RadiusX="15" RadiusY="15" />
</Image.Clip>
</Image>
</Border>
<TextBlock Grid.Column="1" Grid.Row="0" Foreground="{StaticResource ChatClientTextBrush}" Padding="0" Margin="10,0,0,0" FontWeight="Bold" Text="{Binding Name}"></TextBlock>
</Grid>
</Border>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
Then you can simply define ClientDoubleClickCommand
command
in ClientChat
class (like other members of it such as People
):
public class ChatClient: INotifyPropertyChanged
{
//-------- Peopole property --------
.
.
.
//-------- ClientDoubleClickCommand --------
ICommand clientDoubleClickCommand;
public ICommand ClientDoubleClickCommand
{
get
{
return clientDoubleClickCommand ??
(clientDoubleClickCommand = new MyCommand(DoThisOnDoubleClick, true));
}
}
private void DoThisOnDoubleClick(object clientId)
{
// Write your target codes (on Mouse Left Double Click) here:
throw new NotImplementedException();
}
//-------- OTHER PROPERTIES AND CODES OF CLASS--------
.
.
.
}
// MyCommand Class: This class is a technique to implement commands easily
public class MyCommand: ICommand
{
private readonly Action<object> _action;
private readonly bool _canExecute;
public MyCommand(Action<object> action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
}
public class People: INotifyPropertyChanged
{
// ClientId Property:
.
.
.
// ClientName Property:
.
.
.
//-------- OTHER PROPERTIES AND CODES OF CLASS--------
.
.
.
}
In this example, i assumed you have a ClientId
property in your People
class to keep id of each client.
Now you have a People
property, it is a list of clients and you used it like this:
<ItemsControl ItemsSource="{Binding Path=People,........
On the other hand if the ItemsControl
DataContext
be »» ChatClient
class
then we have access to ClientDoubleClickCommand
in it and access to ClientId
in People
class (by ItemsSource
) in the following line inside ItemsControl
block:
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl},Path=DataContext.ClientDoubleClickCommand}"
CommandParameter="{Binding ClientId}"/>
How to bind a ObservableCollection of custom controls to a StackPanel?
MyStatusBar
shouldn't be a UserControl
but a POCO:
public class MyStatusBar : INotifyPropertyChanged { ... }
You could then use an ItemsControl
to bind to the ObservableCollection<MyStatusBar>
and define an ItemTemplate
in where you put the UserControl
to be rendered for each POCO object:
<ItemsControl ItemsSource="{Binding MyUIObjects}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyStatusBarUserControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The ItemsPanelTemplate
specifies the panel - in this case a StackPanel
- that the ItemsPresenter
creates for the layout of the items in the ItemsControl
.
Note that MyStatusBar
and MyStatusBarUserControl
are different types. A view model should not create or expose UI elements. It should expose data objects. You are then creating a UI element per data object using the ItemTemplate
.
Related Topics
Digital Signature in C# Without Using Bouncycastle
JSON.Net Adding Backslash While Returning JSON Serialized String
Best Way to Switch Behavior Based on Type
Blocking Access to Private Member Variables? Force Use of Public Properties
Writing to Output Window of Visual Studio
Why Do We Need the "Event" Keyword While Defining Events
Unique Ways to Use the Null Coalescing Operator
Linq - Query Syntax VS Method Chains & Lambda
Does the Order of Linq Functions Matter
How to Change the Button Text for 'Yes' and 'No' Buttons in the Messagebox.Show Dialog
How to Bind an Enum to a Dropdownlist Control in ASP.NET
ASP.NET Identity Change Password
Use Different Name for Serializing and Deserializing with JSON.Net
How to Check If a Number Is Positive or Negative in C#
Difference Between the System.Array.Copyto() and System.Array.Clone()