Events in Canvas works only for last element - wpf

I use ListView to display list of Buttons. While using StackPanel or WrapPanel as ItemsPanel of the ListView, everything works fine. When I try Canvas only the last one button from a collection is working, others doesn't even change their background when mouse is over them. Can I make all them work normally?
<ListView ItemsSource="{Binding collection1}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Button Margin="{Binding margin}" Width="40" Height="40" Click=button_click />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
code behind:
public class object1
{
public double x0 { get; set; }
public double y0 { get; set; }
public Thickness margin { get { return new Thickness(x0, y0, 0, 0); } }
public object1(double x, double y)
{
x0= x;
y0 = y;
}
}
public MainWindow : Window
{
public ObservableCollection<object1> collection1 {get;set;}
public MainWindow()
{
InitializeComponent();
DataContext = this;
collection1=new ObservableCollection<object1>();
collection1.Add(new object1(20,20));
collection1.Add(new object1(20,80));
collection1.Add(new object1(80,20));
collection1.Add(new object1(80,80)); // ONLY THAT ONE WORKS
}
}

The problem is the way in which you do the positioning of the Buttons with a Margin inside the ListViewItem. All the ListViewItems lie on top of each other in the Canvas, and only the topmost gets input events.
Move the Margin binding out of the ItemTemplate into an ItemContainerStyle, so that it sets the Margin of a ListViewItem instead of a Button:
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="{Binding margin}"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Button Width="40" Height="40" .../>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When using a Canvas as ItemsPanel I'd strongly suggest to use the Canvas.Left and Canvas.Top properties for positioning item containers. You would usually bind them to properties of the data item type, like your x0 and y0:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Canvas.Left" Value="{Binding x0}"/>
<Setter Property="Canvas.Top" Value="{Binding y0}"/>
</Style>
</ListView.ItemContainerStyle>

Related

Wpf ItemsControl with margin coordinates [duplicate]

I want to add a set of rectangles to the main window of my mvvm application. In my viewModel I've got a collection of objects which I convert to System.Windows.Shapes.Rectangle classes with a converter (code below):
ViewModel:
RecognizedValueViewModel
{
public ObservableCollection<BarcodeElement> BarcodeElements
{
get { return _BarcodeElements; }
set { _BarcodeElements = value; }
}
public RecognizedValueViewModel()
{
BarcodeElements = InitializeBarcodeElements();
}
}
Converter:
public BarcodeElementToRectangleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Rectangle barcodeRectangle = GetRectangleFromBarcodeElement(value as BarcodeElement);
return barcodeRectangle;
}
}
The rectangles should be shown in a canvas in my MainWindow:
<Canvas x:Name="Canvas_Image_Main">
<!-- Show rectangles here -->
</Canvas>
I would add Rectangles to canvas in code but I don't now how many rectangles are there at runtime. Is there a way how I can achieve this? Tank you.
In a proper MVVM approach you would have a view model with an abstract representation of a list of rectangles, e.g. like this:
public class RectItem
{
public double X { get; set; }
public double Y { get; set; }
public double Width { get; set; }
public double Height { get; set; }
}
public class ViewModel
{
public ObservableCollection<RectItem> RectItems { get; set; }
}
Then you would have a view that uses an ItemsControl to visualize a collection of such Rect items. The ItemsControl would have a Canvas as its ItemsPanel and an appropriate ItemContainerStyle and ItemTemplate which each bind to the appropriate view model properties. It might look like this:
<ItemsControl ItemsSource="{Binding RectItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Black"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
An alternative without Bindings in Style Setters (which don't work in UWP) might look like this:
<ItemsControl ItemsSource="{Binding RectItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Black">
<Rectangle.RenderTransform>
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can bind the collection of rectangles to an ItemControl and set its height, width and margin:
<ItemsControl ItemsSource="{Binding Path=RectangleCollection,Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Canvas>
<Rectangle Stroke="Black" Heigth={some converter} Width={some converter} Margin={Some Converter}>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemControl>
Just an idea to get you started...

UniformGrid with DataTemplate trigger not working as expected, WPF

I host a list of 64 UserControl in an ItemsControl, the DataContext is an array of objects. Then the DataContext for the individual instance of the UserControl becomes the instance of the object.
The objects have a boolean variable called Exists, this is a DataTemplate trigger to determine if the Usercontrol will be displayed or not.
I use a Uniformgrid to display the list, but I'm experiencing some weird behavior. The Usercontrol don't resize. See attached picture. If I use a StackPanel instead, it works just fine. But I would like to use the UnifromGrid instead.
Here is the code - Only 4 objects have the Exist variable set to true.
<Grid Grid.Row="1" Grid.Column="1" x:Name="gridSome" Background="#FF5AC1F1">
<Viewbox>
<ItemsControl ItemsSource="{Binding SomeVM.SomeModel.SomeArray}"
Margin="15" HorizontalAlignment="Center" VerticalContentAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<tensioner:UCView Margin="5"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Exists}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!--<StackPanel IsItemsHost="true"/> This works-->
<UniformGrid Columns="1"/> <!-- This does not work-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Viewbox>
</Grid>
-----Update------
//SSCCE
MainWindow
<Window x:Class="WpfAppItemIssue.MainWindow"
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"
xmlns:local="clr-namespace:WpfAppItemIssue"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<!--<Viewbox>-->
<ItemsControl ItemsSource="{Binding Model.Cars}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="ABC"></TextBox>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding exists}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!--</Viewbox>-->
</Grid>
</Window>
MainViewModel
using System.ComponentModel;
namespace WpfAppItemIssue
{
class MainViewModel:INotifyPropertyChanged
{
public MainViewModel()
{
Model = new MainModel();
}
private MainModel model;
public MainModel Model
{
get
{
return model;
}
set
{
model = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
Model
namespace WpfAppItemIssue
{
class MainModel
{
public Car[] Cars { get; set; }
public MainModel()
{
Cars = new Car[64];
for (int i = 0; i < Cars.Length; i++)
{
Cars[i] = new Car(i);
}
}
}
internal class Car
{
public int someVal { get; set; }
public bool exists { get; set; }
public Car(int someVal)
{
this.someVal = someVal;
if (someVal < 5) //Just enable few items for debug
{
exists = true;
}
else
{
exists = false;
}
}
}
}
See attached images :
Picture 1 shows Design View. Why are the user controls not being resized?
Picture 2 shows On Execute. Why are the user controls not being resized?
Picture 3 shows On Any resize event. The Controls are being resized correctly.
Well I finally got your problem after discussion in comments. It is all about DataTrigger in your ItemTemplate. Just move it to ItemContainerStyle Triggers and elements will be resized correctly.
<ItemsControl ItemsSource="{Binding Model.Cars}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Style.Triggers>
<DataTrigger Binding="{Binding exists}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="ABC"></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that TextBox'es will be resized "by border" only (this behavior is shown on your last picture), font size will not be changed. If you want to scale your elements uniformly with their content you really need to wrap ItemsControl to Viewbox.
This is not a weird behavior, but it's the way the UniformGrid works.
As an ItemsPanel of the ItemsControl, the UniformGrid uses the ItemSource collection to determine the row count and the column count. It doesn't matter whether the items that will be placed in the UniformGrid are visible or not - all the grid cells have the same width and height. So your DataTrigger has no effect on the layout of the UniformGrid, it only affects the visibility of the items.
The StackPanel works in a different way. There are no cells, the StackPanel arranges all the visible items in such a way that they occupy the available space.

Horizontally scroll all rows within column at the same time using one scrollbar

How can i horizontally scroll a listview within single column in a DataGrid, using a scrollbar that is external to the DataGrid, ensuring that each row scrolls at the same time?
(I would also like the starting position of the scrollbar to be all the way to the right, so that the last item in the listview is visible when the data is loaded)
I cannot use and third-party libraries or dlls.
Any help would be greatly appreciated.
Many thanks
EDIT
First column sample code
<DataGridTemplateColumn Width="325" Header="Header Text" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding TrendList}"
BorderThickness="0"
Background="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center"
>
<ListBox.Resources>
<Style TargetType="ScrollViewer">
<Setter Property="resex:ScrollSynchronizer.ScrollGroup" Value="Group1" />
<Setter Property="resex:ScrollSynchronizer.AlwaysScrollToEnd" Value="True" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Margin="2,2,2,2" Width="25" Height="25"
HorizontalAlignment="Center" VerticalAlignment="Center"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Content="{Binding ArisingList.Count}"
FontWeight="Bold
>
<interact:Interaction.Triggers>
<interact:EventTrigger EventName="MouseDoubleClick">
<interact:InvokeCommandAction Command="{Binding OpenPCICommand}"
CommandParameter="{Binding ArisingList}" />
</interact:EventTrigger>
</interact:Interaction.Triggers>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Background" Value="Green"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ArisingList.Count}" Value="0">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
<Label.ToolTip>
<TextBlock x:Name="pciID" Text="{Binding PciID, StringFormat='PCI Number : {0}'}" />
</Label.ToolTip>
</Label>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</DataTemplate>
This is what i have so far. The scrollbars are all linked internally but they arent linked to any external scrollbar
I managed to create a synchronous scroll behavior using Attached properties
may this help you achieve your goal in some way
for this I have made some changes to your template as well
ListBox to ItemsControl
Wrapped ItemsControl in a ScrollViewer
Added ScrollHelper.IsSyncScrollEnabled & ScrollHelper.ScrollValue to ScrollViewer
added a Name to main DataGrid
sample xaml
<Grid xmlns:l="clr-namespace:CSharpWPF">
<DataGrid x:Name="dGrid" ItemsSource="{Binding MyDataSource}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="325"
Header="Header Text">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Hidden"
l:ScrollHelper.IsSyncScrollEnabled="true"
l:ScrollHelper.ScrollValue="{Binding (l:ScrollHelper.ScrollValue),ElementName=dGrid,Mode=TwoWay}">
<ItemsControl ItemsSource="{Binding TrendList}">
<ItemsControl.ItemTemplate>
... your item template here
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
... other columns etc
</DataGrid.Columns>
</DataGrid>
</Grid>
ScrollHelper class
namespace CSharpWPF
{
class ScrollHelper : DependencyObject
{
public static bool GetIsSyncScrollEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsSyncScrollEnabledProperty);
}
public static void SetIsSyncScrollEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsSyncScrollEnabledProperty, value);
}
// Using a DependencyProperty as the backing store for IsSyncScrollEnabled. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSyncScrollEnabledProperty =
DependencyProperty.RegisterAttached("IsSyncScrollEnabled", typeof(bool), typeof(ScrollHelper), new PropertyMetadata(false, OnIsSyncScrollEnabledChanged));
private static void OnIsSyncScrollEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer sv = d as ScrollViewer;
if ((bool)e.NewValue)
sv.ScrollChanged += sv_ScrollChanged;
else
sv.ScrollChanged -= sv_ScrollChanged;
}
static void sv_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.HorizontalChange != 0)
{
ScrollViewer sv = sender as ScrollViewer;
SetScrollValue(sv, sv.HorizontalOffset);
}
}
public static double GetScrollValue(DependencyObject obj)
{
return (double)obj.GetValue(ScrollValueProperty);
}
public static void SetScrollValue(DependencyObject obj, double value)
{
obj.SetValue(ScrollValueProperty, value);
}
// Using a DependencyProperty as the backing store for ScrollValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ScrollValueProperty =
DependencyProperty.RegisterAttached("ScrollValue", typeof(double), typeof(ScrollHelper), new PropertyMetadata(0.0, OnScrollValueChange));
private static void OnScrollValueChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer sv = d as ScrollViewer;
if (sv != null)
{
double offset = (double)e.NewValue;
if (sv.HorizontalOffset != offset)
sv.ScrollToHorizontalOffset(offset);
}
}
}
}
result is synchronous scrolling between all of the scrollviewers
Extra
if you do not want scroll bar to appear on all rows you can make use of triggers
eg
<Grid xmlns:l="clr-namespace:CSharpWPF">
<DataGrid x:Name="dGrid">
<DataGrid.Columns>
<DataGridTemplateColumn Width="325"
Header="Header Text">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
x:Name="scroll"
l:ScrollHelper.IsSyncScrollEnabled="true"
l:ScrollHelper.ScrollValue="{Binding (l:ScrollHelper.ScrollValue),ElementName=dGrid,Mode=TwoWay}">
<ItemsControl ItemsSource="{Binding TrendList}">
<ItemsControl.ItemTemplate>
... your template here
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="scroll"
Property="HorizontalScrollBarVisibility"
Value="auto" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
result is a single scroll bar scrolls all the rows data
EDIT
to get the default scroll position to show the last item in the itemscontrol, change the method sv_ScrollChanged as follows
static void sv_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = sender as ScrollViewer;
if (e.HorizontalChange != 0)
{
SetScrollValue(sv, sv.HorizontalOffset);
}
if (e.ExtentWidthChange != 0)
{
SetScrollValue(sv, e.ExtentWidth);
}
}
If I understand you correctly, you want to be able to scroll across the Width of your DataGrid, but not using the DataGrid ScrollBars. As long as there is nothing restricting the Width of the DataGrid, then you can do this easily with a ScrollViewer. Try this:
<ScrollViewer>
<DataGrid ItemsSource="{Binding YourCollectionProperty}" />
</ScrollViewer>

MouseDragElementBehavior in DataTemplate

So here's an issue that I'm having. I'm trying to use MouseDragElementBehavior in listbox. I was able to make it work when I was creating items in listbox directly, as in this example:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Items>
<Border Width="20" Height="20">
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</i:Interaction.Behaviors>
<Rectangle Fill="Red"/>
</Border>
</ItemsControl.Items>
</ItemsControl>
But as soon as I've started using DataTemplate, it stopped working.
<ItemsControl Grid.Column="1" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Items>
Test item
</ItemsControl.Items>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="20" Height="20">
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</i:Interaction.Behaviors>
<Rectangle Fill="Red"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Any ideas as to why? I can't really figure out how a DataTemplate would affect MouseDragElementBehavior.
The MouseDragElementBehavior acts upon the FrameworkElement you attach it to. In your case it is the Border element which will be contained by a ContentPresenter which is the container generated by the ItemsControl. You have set ConstrainToParentBounds="True" which will ensure the visual will not be displayed outside its container, in this case the ContentPresenter. There are a few options, some easy, one probably not worth undertaking (but I did to figure some stuff out).
Set ConstrainToParentBounds="False". I am supposing that you don't want the Border to leave the ItemsControl so this probably won't suit.
Set the ItemContainerStyle to a Style which sets the Template to and adds the interaction to a similarly configured ContentPresenter. The base implementation of the ItemsControl uses a vanilla ContentPresenter. A caveat here is that if you aren't using UI elements as items you will need to wrap the item in one using a custom items control (see this answer on setting the container style):
<ItemsControl Grid.Column="1" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<ContentPresenter>
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</i:Interaction.Behaviors>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style >
</ItemsControl.ItemContainerStyle>
<ItemsControl.Items>
Test item
</ItemsControl.Items>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="20" Height="20">
<Rectangle Fill="Red"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Attach the interaction using the ItemsControl.ItemContainerStyle. This is a little involved because the Interaction.Behaviors attached property only has a setter:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="beh:AddCollectionsToSetter.Behaviors">
<Setter.Value>
<beh:BehaviorCollection>
<ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</beh:BehaviorCollection>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
For this I had to create a separate attached property AddCollectionsToSetter.Behaviors which is read/write and a BehaviorCollection that allows the interactions to be added to.
public static class AddCollectionsToSetter
{
#region Behaviors Dependency Property (Attached)
/// <summary>Gets the behaviours to add.</summary>
public static BehaviorCollection GetBehaviors(DependencyObject obj)
{
return (BehaviorCollection)obj.GetValue(BehaviorsProperty);
}
/// <summary>Sets the behaviours to add.</summary>
public static void SetBehaviors(DependencyObject obj, BehaviorCollection value)
{
obj.SetValue(AddCollectionsToSetter.BehaviorsProperty, value);
}
/// <summary>DependencyProperty backing store for <see cref="Behaviors"/>. Represents the behaviours to add.</summary>
/// <remarks></remarks>
public static readonly DependencyProperty BehaviorsProperty =
DependencyProperty.RegisterAttached("Behaviors", typeof(BehaviorCollection), typeof(AddCollectionsToSetter), new PropertyMetadata(null, BehaviorsPropertyChanged));
private static void BehaviorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var oldBehaviors = (BehaviorCollection)e.OldValue;
var newBehaviors = (BehaviorCollection)e.NewValue;
var interaction = Interaction.GetBehaviors(d);
interaction.RemoveRange(oldBehaviors); // extension method, simple iterate and remove
interaction.AddRange(newBehaviors.Clone()); // extension method, simple iterate and add
}
#endregion Behaviors Dependency Property (Attached)
}
public class BehaviorCollection : FreezableCollection<System.Windows.Interactivity.Behavior>
{
public BehaviorCollection()
: base()
{
}
public BehaviorCollection(int capacity)
: base(capacity)
{
}
public BehaviorCollection(IEnumerable<System.Windows.Interactivity.Behavior> behaviors)
: base(behaviors)
{
}
}

How to loop through ItemsControl in WPF?

How can I loop through this ItemsControl and change it's TextBlock background in this Xaml's code behind page on some mouse event. I am new to WPF.
<ItemsControl ItemsSource="{Binding Path= HeaderList}" Name="Headers">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Name="Data" Text="{Binding }" Width="100" HorizontalAlignment="Left" PreviewMouseLeftButtonDown="MouseLeftButtonDown_Handler"
MouseEnter="MouseEnter_Handler" MouseLeave="MouseLeave_Handler">
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks in advance!!
Actually my requirement is to change individual TextBlock's background color on different mouse events. So i need to get access of TextBlock in code behind and depending upon login I can change that Textblock's background color accordingly. So i think need to iterate ItemsControl. in case if I bind Background Property then all on property change would have effect on all the Textblocks in that ItemsControl. I don't want it in this way. I want to set and change every individual textblock's color differently.
I have access to single one in the eventhandlers that caused that event, but I want to access all the textblocks that are in itemscontrol and change their color acoording to some logic
Solution with background binding like axelle suggested:
You can iterate through the items in the HeaderList and set the background-property.
The Header class must implement the INotifyPropertyChanged Interface
<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">
<ItemsControl ItemsSource="{Binding Path=HeaderList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Background="{Binding Background}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public partial class MainWindow : Window
{
public class Header : NotificationObject
{
public string Text { get; set; }
public Brush Background { get; set; }
}
public IList<Header> HeaderList { get; set; }
public MainWindow()
{
HeaderList = new List<Header>
{
new Header {Text = "header1", Background = Brushes.Red},
new Header {Text = "header2", Background = Brushes.Blue},
new Header {Text = "header3", Background = Brushes.Chartreuse},
};
DataContext = this;
InitializeComponent();
}
}
If I understand your question correctly, you'd want to bind the TextBlock background to a value in your datacontext, and change that value on your mouse event.
don't loop through the itemscontrol, better use a Trigger to apply the changes to your textblock :)
<ItemsControl ItemsSource="{Binding Path= HeaderList}" Name="Headers">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>

Resources