Canvas Children Property Binding - c#

I'm learning wpf and how to properly bind. This is the code I am working on:
<ItemsControl ItemsSource="{Binding CanvasChildren}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type espace:Entity}" />
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
<Setter Property="Canvas.ZIndex" Value="{Binding Z}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
So you can see I'm binding CanvasChildren in this ItemsControl. CanvasChildren is an ObservableCollection of type "Entity". Entity is my own class and it's another canvas object that will have images and such in it.
Entity has properties X,Y,Z in it and I WANT to be able to bind those properties to the Canvas.Left,Canvas.Top,Canvas.ZIndex but I am TOTALLY lost on how to do that. The style setters I have defined here do NOT WORK.
For one the binding values are checking for X,Y,Z coordinates in my viewmodel which is defined:
<base:SceneBase.DataContext>
<sceneGame:SceneGameViewModel />
</base:SceneBase.DataContext>
But changing the setters to something like:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=X}" />
<Setter Property="Canvas.Top" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=Y}" />
<Setter Property="Canvas.ZIndex" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=Z}" />
</Style>
</ItemsControl.ItemContainerStyle>
does not work either.
It may possibly be something simple that I'm overlooking but I'm still learning WPF and I'm lost.

If you use an ItemsControl with an item type that is a UIElement (which is a bit unusual), the control will not create an additional item container - i.e. a ContentPresenter - element for it, but instead apply the ItemContainerStyle directly to the item. You can verify this by setting TargetType="espace:Entity" on the Style.
In this case, the ItemsControl does also not set the DataContext of the UIElement item, which means that Bindings without an explictly set source won't work. The Bindings in the ItemContainerStyle would use the item object directly as its source, i.e. use RelativeSource Self.
It is also useless to declare a DataTemplate for the item type (especially an empty one), because it would be ignored. The item is not considered to be "data", but UI.
<ItemsControl ItemsSource="{Binding CanvasChildren}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="espace:Entity">
<Setter
Property="Canvas.Left"
Value="{Binding X, RelativeSource={RelativeSource Self}}"/>
<Setter
Property="Canvas.Top"
Value="{Binding Y, RelativeSource={RelativeSource Self}}"/>
<Setter
Property="Panel.ZIndex"
Value="{Binding Z, RelativeSource={RelativeSource Self}}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>

Related

Unable to bind item property in itemcontainerstyle

I created a .NET WPF Application with MvvmLight this evening. I'm trying to show a couple of circles on my main window.
My MainWindow.xaml is bound to MainViewModel.cs. MainViewModel contains an ObservableCollection with SpaceObjects. I'm trying to show this collection using itemcontrols.
<ItemsControl ItemsSource="{Binding SpaceObjects}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding }"></Setter>
<Setter Property="Canvas.Top" Value="{Binding Radius}"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="{Binding Radius}" Height="{Binding Radius}" Fill="Blue" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In the ItemTemplate I am able to bind to a SpaceObject property (like Radius). In ItemContainerStyle I can only choose from the context:
I would like to choose from the items in the source (ItemsScourse SpaceObjects), so I can bind the X and Y coordinates from the circle. What am I doing wrong?
<DataGrid.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<d:Style.DataContext>
<x:Type Type="SpaceObject" />
</d:Style.DataContext>
<Setter Property="Canvas.Left" Value="{Binding }"/>
<Setter Property="Canvas.Top" Value="{Binding Radius}"/>
</Style>
</DataGrid.ItemContainerStyle>

WPF + MVVM: Override autocomplete in DataGridComboBoxColumn

I've been searching for quite a while, but cannot figure out how to override the autocomplete functionality in a DataGridComboBoxColumn.
What I want to do is what is explained here, except for a combobox: Searching for items in a list box
That is: I want to be able to enter any string and then apply a filter to the ComboBox items in the DataGridComboBoxColumn to show only those items that match this as a substring.
I'm new to WPF and have been searching online for a while. I've found things like EventSetters and CommandBehaviorCollection.Behaviors, but I can't get a clear picture of the possibilities (and impossibilities).
I've got:
<DataGrid ... >
...
<DataGrid.Columns>
...
<MyCustomDataGridComboBoxColumn Header="My Header" MinWidth="200" >
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding DataContext.MyData, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<Setter Property="SelectedItem" Value="{Binding DataItem, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True}" />
<Setter Property="DisplayMemberPath" Value="HardwareId" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding DataContext.MyFilteredData, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<Setter Property="SelectedItem" Value="{Binding DataItem, UpdateSourceTrigger=LostFocus}" />
<Setter Property="DisplayMemberPath" Value="HardwareId" />
<Setter Property="IsEditable" Value="True"/>
<Setter Property="Text" Value="{Binding DataContext.MyNewDataItem, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</MyCustomDataGridComboBoxColumn>
...
</DataGrid.Columns>
</DataGrid>
Ideally, I'd like to create a new class that inherits from DataGridComboBoxColumn and supply it with some custom logic, such as supplying an anonymous function in its constructor so that the autocomplete behavior can be overridden in different ways in the future.
Is this even possible, or am I going about this entirely the wrong way?
I'm not saying your approach is wrong, however, I would approach it differently. For me it seems easier to use a DataGridTemplateColumn and supply a ComboBox that has the functionality you speak of.
<DataGridTemplateColumn Header="ColumnName" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<YourCustomComboBox/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Edit:
A while ago I needed a ComboBox with the same functionality. I ended up combining a TextBox with a Popup control because it gave me much more control over it.
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" x:Name="editBox"/>
<Popup x:Name="textboxPopup" Width="{Binding ElementName=editBox, Path=ActualWidth, Mode=OneWay}"
PlacementTarget="{Binding ElementName=editBox}"
StaysOpen="False"
IsOpen="{Binding Path=IsOpen, Mode=OneWay}">
<Grid>
<DockPanel MaxHeight="500">
<ListView SelectionMode="Single"
ItemsSource="{Binding Path=Suggestions}"
Name="popupList">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"Color="LightBlue"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="LightBlue"/>
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</DockPanel>
</Grid>
In the codebehind I subscribed to the TextChanged event and a few other events that came in handy. I can't share all my code because it is production code. However there are a few other people on the internet with similar implementations: using a ComboBox, using a textbox and of course the link you posted in your question. There is more than enough out there.
And about using your custom control as a TargetType... I don't see anything wrong with that, I do it all the time.
The error with the CellTemplate shouldn't occur. Are you using it correctly? See this link for an example.

WPF Performance issue with ItemsControl

I use MVVM in my project and i have itemscontrol with grid as panel
<ItemsControl
ItemsSource="{Binding Items}"
ItemTemplateSelector="{StaticResource TemplateSelector}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter
Property="Grid.Row"
Value="{Binding Row}" />
<Setter
Property="Grid.Column"
Value="{Binding Column}" />
<Setter
Property="Grid.ColumnSpan"
Value="{Binding HorizontalSize}" />
<Setter
Property="Grid.RowSpan"
Value="{Binding VerticalSize}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid
ShowGridLines="False"
wpflib:GridHelpers.ColumnCount="{Binding Columns, Mode=OneWay}"
wpflib:GridHelpers.RowCount="{Binding Rows, Mode=OneWay}"
wpflib:GridHelpers.AutoRows="{Binding AutoRowsFormatted, Mode=OneWay}"
wpflib:GridHelpers.AutoColumns="{Binding AutoColumnsFormatted, Mode=OneWay}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
And items templates are buttons with custom styles.
I am trying to display only like 20 items in grid but i have performance issues when items are added to my view model!
currentPage.Items = observable;
It takes someting like 2 seconds to display those items. Any ideas how to make this faster?

Trigger a binding update in XAML?

My XAML set up is a bit complex code:
<UserControl.Resources>
<local:SuperCoolObject x:Key="firstObject"/>
<local:TotallyHotObject x:Key="secondObject"/>
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=MyItems, Source={StaticResource firstObject}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<lol:MyConverter x:Key="myConverter" Equals="{Binding Path=SelectedItem, Source={StaticResource secondObject}}" />
<Style x:Key="On" TargetType="local:ThemedImage">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource myConverter}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataTemplate.Resources>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
(I've omitted the actual DataTemplate)
Notice how the ItemsControl's ItemsSource binds to firstObject and myConverter binds to secondObject. What I need to do is have the ItemsSource binding refresh when the myConverter binding (secondObject.SelectedItem) property changes.
All the appropriate properties are set up as binding properties but I need some way or forcing ItemsSource to refresh in XAML.

ItemsPanelTemplate Selector in wpf?

I need to set the ItemsPanelTemplate property of a listbox based on a dependency property on the control. How do I use the DataTemplateSelector to do that?
I have something like:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<!-- Here I need to replace with either a StackPanel or a wrap panel-->
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Thanks
There isn't an ItemsPanelSelector (probably because it isn't a DataTemplate) but you can bind it or use a Trigger
Binding example
<ListBox ItemsPanel="{Binding RelativeSource={RelativeSource Self},
Path=Background,
Converter={StaticResource MyItemsPanelConverter}}">
Trigger in Style example
<ListBox ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- Your Trigger.. -->
<Trigger Property="Background" Value="Green">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
I'm thinking the best route here would be to use a Style for your ListBox and set Triggers that change the ItemsPanel based on the DependencyProperty you reference.

Categories