Binding to Custom Control Inside Datatemplate for Itemscontrol

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



Leave a reply



Submit