How to select items in a custom control - c#

I wrote a custom control to displays items from an ItemsSource at calculated positions.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Control="clr-namespace:MyControls.Control">
<Style TargetType="{x:Type Control:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control:MyControl}">
<Border x:Name="DrawingArea"
BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Padding="{TemplateBinding Control.Padding}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="true">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Canvas IsItemsHost="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ClipToBounds="true" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
...
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MyControl.cs
public class MyControl : MultiSelector, INotifyPropertyChanged
{
private bool _layoutHandlingDone = false;
private Border _drawingArea;
private Border DrawingArea
{
get { return _drawingArea ?? (_drawingArea = (Border) Template.FindName("DrawingArea", this)); }
}
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
}
protected override Size MeasureOverride(Size aviableSize) { ... }
protected override Size ArrangeOverride(Size finalSize) { ... }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (Template != null && _layoutHandlingDone == false)
{
_layoutHandlingDone = true;
ItemContainerGenerator.StatusChanged += (sender, args) =>
{
if(ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
InvalidateMeasure();
};
DrawingArea.SizeChanged += (sender, args) => UpdateLayout();
}
}
}
Layouting and drawing the items works very well. Now I want the items to be selectable. For this I derived my control form MultiSelector, but it seems that this isn't enough.
What functionality does the MultiSelector provide and how should I use it in my control?

I strongly suggest to separate things here. Managing a collection of items and arranging them in a container control are two distinct concerns, and ItemsControl provides support for both.
Instead of overriding MeasureOverride and ArrangeOverride, you should write your specialized Panel class (and override MeasureOverride and ArrangeOverride there) and use that class in the ItemsPanel template of your ItemsControl.
You could then easily derive from ListBox instead of MultiSelector and would get all the selection functionality for free.

Related

A class derived Control that has a Canvas in template, I want to work like canvas

I have a class Tableau derived from Windows.Controls.Control, its template contains a Canvas:
<Style TargetType="{x:Type local:Tableau}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Tableau}">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ScrollViewer x:Name="ScrollViewer"
HorizontalScrollBarVisibility="{Binding RelativeSource={RelativeSource TemplatedParent},Path=HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{Binding RelativeSource={RelativeSource TemplatedParent},Path=VerticalScrollBarVisibility}"
>
<Canvas x:Name="Diagram"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}"
>
<Canvas.RenderTransform>
<ScaleTransform CenterX="0"
CenterY="0"
ScaleX="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Scale}"
ScaleY="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Scale}"
/>
</Canvas.RenderTransform>
</Canvas>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and Tableau class is:
public class Tableau:Control
{
private Canvas _canvas;
public Canvas Canvas
{
get { return _canvas; }
}
static Tableau()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(Tableau), new FrameworkPropertyMetadata(typeof(Tableau)));
}
public Tableau()
: base()
{
this.Loaded += Tableau_Loaded;
}
private void Tableau_Loaded(object sender, RoutedEventArgs e)
{
_canvas = (Canvas)this.Template.FindName("Diagram", this);
}
}
I want to use this as a canvas, something like this:
<local:Tableau>
<Rectangle Fill="AliceBlue" Stroke="Black" Width="100" Height="100" Canvas.Top="10"/>
<Rectangle Fill="AliceBlue" Stroke="Black" Width="100" Height="100" Canvas.Left="130" Canvas.Top="50"/>
</local:Tableau>
It is possible or not, if yes how?
You could define a property Children, have it return the Children collection from the internal Canvas, and apply the ContentProperty attribute. Here's a modified version of the class I tested:
[ContentProperty(nameof(Children))]
public class Tableau : Control
{
static Tableau()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(Tableau), new FrameworkPropertyMetadata(typeof(Tableau)));
}
public Tableau() : base()
{
UpdateDefaultStyle();
}
private Canvas canvas;
public Canvas Canvas
{
get
{
if (canvas == null) { ApplyTemplate(); }
return canvas;
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
canvas = (Canvas)GetTemplateChild("Diagram");
}
public UIElementCollection Children
{
get { return Canvas.Children; }
}
}
Note that I use OnApplyTemplate instead of Loaded, so that I can get the internal Canvas (and thus the Children collection) even before the Tableau loads. I also use UpdateDefaultStyle in the constructor to ensure the default template is available to load as early as possible.

Binding attached property to attached property inside a control template

Here is a working example of my issue: https://github.com/themimcompany/AttachedPropertyIssue
I have a Style for a Button that defines a ControlTemplate. Inside the ControlTemplate I have a TextBlock that has the same attached property. I want to set/bind the TextBlock's attached property to the value of the Button's attached property. The attached property is a simple int value.
How do I get this working? Is this possible to do in UWP? I get errors that don't give me indication on how to fix this. Like Unspecified error...
Here is my Style:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock local:Translation.TranslationModelID="{Binding Path=(local:Translation.TranslationModelID),
RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is my attached property definition:
public class Translation
{
public static int GetTranslationModelID(DependencyObject obj)
{
return (int)obj.GetValue(TranslationModelIDProperty);
}
public static void SetTranslationModelID(DependencyObject obj, int value)
{
obj.SetValue(TranslationModelIDProperty, value);
}
public static readonly DependencyProperty TranslationModelIDProperty =
DependencyProperty.RegisterAttached("TranslationModelID", typeof(int), typeof(FrameworkElement), new PropertyMetadata(0));
}
Here is the button I'm trying to define, notice I want to assign the attached property of the button- then take the value in the ControlTemplate and assign it to the TextBlock's attached property (look at the style):
<Button local:Translation.TranslationModelID="1" />
Explaining once more: I have an attached property assigned to a Button and I would like to assign the value of that Button's attached property to the same attached property of a TextBlockin the ControlTemplate of that Button's Style. It's not working as I expect. I get Unspecified error.. exception at run time. How do I get this working?
Here is a working example of my issue: https://github.com/themimcompany/AttachedPropertyIssue
EDIT:
This approach is giving me a different error. The property 'TranslationModelID' was not found in type 'PassingAttachedPropertyInControlTemplater.Translation'. [Line: 17 Position: 40]
<Page.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock local:Translation.TranslationModelID="{TemplateBinding local:Translation.TranslationModelID}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<Button x:Name="btn" local:Translation.TranslationModelID="1" />
</Grid>
I want to assign an attached property of the templated parent to an attached property of an element inside a ControlTemplate using TemplateBinding or regular Binding
For your requirement, you could create Templated Control that inherit button. Then set TextBlock Attached property in the code behind. Please check the follow segment code.
public sealed class CustomButton : Button, IDisposable
{
private long token;
private TextBlock Tbk;
public CustomButton()
{
this.DefaultStyleKey = typeof(CustomButton);
}
private void CallBackMethod(DependencyObject sender, DependencyProperty dp)
{
UpdateAttachedPropertyValue();
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
token = this.RegisterPropertyChangedCallback(AttachedPropertyTest.Translation.TranslationModelIDProperty, CallBackMethod);
Tbk = GetTemplateChild("TBK") as TextBlock;
UpdateAttachedPropertyValue();
}
public void Dispose()
{
this.UnregisterPropertyChangedCallback(AttachedPropertyTest.Translation.TranslationModelIDProperty,token);
}
private void UpdateAttachedPropertyValue()
{
AttachedPropertyTest.Translation.SetTranslationModelID(Tbk, AttachedPropertyTest.Translation.GetTranslationModelID(this));
}
}
Xaml
<Style TargetType="local:CustomButton" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomButton">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<TextBlock x:Name="TBK"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

UWP TemplateBinding in ControlTemplate to fixed value

I want to bind a property (myHeight) in my Controltemplate to the parent. The following is my code so far.
Resource dict
<Style TargetType="local2:TestingControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:TestingControl">
<Border
Height="{TemplateBinding myHeight}"
Background="Green"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<ContentPresenter Content="{TemplateBinding Content}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TestingControl.cs
[ContentProperty(Name = "Content")]
public sealed class TestingControl : Control
{
public TestingControl()
{
this.DefaultStyleKey = typeof(TestingControl);
}
public static readonly double myHeight = (double)100;
public object Content
{
get { return (string)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(string), typeof(TestingControl), new PropertyMetadata(string.Empty));
}
What i'm trying to bind is the myHeight. I'd like to have this in the .cs since I need to run some operations on it. This fails to load entirely!
I also tried the following approach
Resource dict
<x:Double x:Key="myHeight">100</x:Double>
<Style TargetType="local2:TestingControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:TestingControl">
<Border
Height="{ThemeResource myHeight}"
Background="Green"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TestingControl.cs
[ContentProperty(Name = "Content")]
public sealed class TestingControl : Control
{
public TestingControl()
{
this.DefaultStyleKey = typeof(TestingControl);
var x = (double)Resources["myHeight"];
}
public object Content
{
get { return (string)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(string), typeof(TestingControl), new PropertyMetadata(string.Empty));
}
The problem with the second approach, is that when reading the property in the .cs code, var x = (double)Resources["myHeight"]; I get an exception.
Resolutions to either (preferably both, since I'm just trying to learn UWP) would be greatly appreciated.
The first thing is TemplateBinding should bind the dependency property and you write the static filed that can not bind to Height.
The second thing is ThemeResource will find the Theme but you define a static source.
<x:Double x:Key="myHeight">100</x:Double>
<Style TargetType="local2:TestingControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:TestingControl">
<Border
Height="{StaticResource myHeight}"
Background="Green"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The third thing is you get the resource at first but the resource inits after OnApplyTemplate.
You should move the code that gets the resource to OnApplyTemplate.
protected override void OnApplyTemplate()
{
try
{
// find something in TestingControl.Resources
var x = Resources["myHeight"];
}
catch (Exception e)
{
}
try
{
// find something in App.Resources
var x = App.Current.Resources["myHeight"];
}
catch (Exception e)
{
}
base.OnApplyTemplate();
}
If your resource is written in App.xaml that you should use App.Current.Resources to get the resource.
If you want to get the resource in your customs control that you should add the resource in your control.
<local:TestingControl>
<local:TestingControl.Resources>
<x:Double x:Key="myHeight">100</x:Double>
</local:TestingControl.Resources>
</local:TestingControl>

WPF Custom control binding image source

I have a custom control based on a button, and I put an image inside. I can set the source of the image in the xaml, but if I try and bind it, it doesn't work.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl">
<Style TargetType="{x:Type local:MyCustomControl}" BasedOn = "{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="pathname"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This works just fine, however if I replace the <Image Source="pathname"/> with <Image Source={Binding MyImage, RelativeSource={RelativeSource Self}}"/>, and reference a Delegate Property in the class, it breaks.
MyCustomControl.cs
public class MyCustomControl : Button
{
static DependencyProperty m_myimage = null;
private DependencyProperty MyImageProperty
{
get
{
return m_myimage;
}
}
public BitmapImage MyImage
{
get
{
return (BitmapImage)GetValue(MyImageProperty);
}
set
{
SetValue(MyImageProperty, value);
}
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
MyImage = new BitmapImage(new Uri(pathname));
}
private static void RegisterDependencyProperties()
{
if (m_myimage == null)
{
m_myimage = DependencyProperty.Register("MyImage",
typeof(BitmapImage), typeof(MyCustomControl), null);
}
}
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
new FrameworkPropertyMetadata(typeof(MyCustomControl)));
RegisterDependencyProperties();
}
}
How can I get it to work?
Figured it out myself after about 2 hours. The xaml binding should look like,
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="{Binding MyImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Note that the binding relative resource went from RelativeSource={RelativeSource Self} to RelativeSource={RelativeSource TemplatedParent}

Nested UserControls DataContext

Abstract:
I have two UserControls named Zone and ZoneGroup. One of these controls (ZoneGroup) includes two instances of the other one (Zone). Both of them set DataContext of the root element to this, at the Loaded event-handler.
The problem is that DataContext of inner controls (Zones) is set before loading (the DataContextChanged event occurred before Loaded) which causes some malfunctions in UI. (inside Zone controls initial state is wrong.) If I prevent it, everything works fine (at least seems to be!) except I encounter with the following error report. (In the Output window)
public partial class Zone : UserControl
{
∙∙∙
private void Zone_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Adding this if-clause solve UI problems but makes some binding errors!
if (this.IsLoaded)
brdRoot.DataContext = this;
}
}
System.Windows.Data Error: 40 : BindingExpression path error: 'ZoneBrush' property not found on 'object' ''ZoneGroup' (Name='')'. BindingExpression:Path=ZoneBrush; DataItem='ZoneGroup' (Name=''); target element is 'brdRoot' (Name=''); target property is 'BorderBrush' (type 'Brush')
Details:
There is a UserControl named Zone containing several data-bindings like so..
<UserControl x:Class="MyApp.Zone"
∙∙∙>
<Border x:Name="brdRoot" BorderBrush="{Binding ZoneBrush}" BorderThickness="1">
∙∙∙
</Border>
</UserControl>
So, I set brdRoot data-context as
public partial class Zone : UserControl
{
public Brush ZoneBrush
{
get { return (Brush)GetValue(ZoneBrushProperty); }
set { SetValue(ZoneBrushProperty, value); }
}
∙∙∙
public Zone()
{
InitializeComponent();
}
private void Zone_Loaded(object sender, RoutedEventArgs e)
{
brdRoot.DataContext = this;
}
∙∙∙
}
Also, there is another UserControl that has two ContentPresenters in order to contain and manage two Zone controls.
<UserControl x:Class="MyApp.ZoneGroup"
∙∙∙>
<Border x:Name="brdRoot" BorderBrush="Gray" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding MainZone}"
Margin="{Binding MainZonePadding}"/>
<ContentPresenter Content="{Binding MemberZone}"/>
</StackPanel>
</Border>
</UserControl>
And the code-behind is:
public partial class ZoneGroup : UserControl
{
public Thickness MainZonePadding
{
get { return (Thickness)GetValue(MainZonePaddingProperty); }
set { SetValue(MainZonePaddingProperty, value); }
}
public Zone MainZone
{
get { return (Zone)GetValue(MainZoneProperty); }
set { SetValue(MainZoneProperty, value); }
}
public Zone MemberZone
{
get { return (Zone)GetValue(MemberZoneProperty); }
set { SetValue(MemberZoneProperty, value); }
}
public ZoneGroup()
{
InitializeComponent();
}
private void ZoneGroup_Loaded(object sender, RoutedEventArgs e)
{
brdRoot.DataContext = this;
}
∙∙∙
}
Edit ► Sketch:
My app works fine as expected, but some BindingExpression errors are reported.
You're overcomplicating it all too much, with all those UserControls and all those DependencyProperties. Look at this sample which uses 0 lines of C# code (XAML-Only):
<Window x:Class="MiscSamples.ItemsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ItemsControl" Height="300" Width="300">
<Window.Resources>
Style for the ItemsControl:
<Style TargetType="ItemsControl" x:Key="ZoneItemsControlStyle">
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="5">
<DockPanel>
<TextBlock HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Text="{Binding Items.Count,RelativeSource={RelativeSource TemplatedParent}, StringFormat='{}{0} Item(s)'}"
Foreground="{TemplateBinding Foreground}"
DockPanel.Dock="Top"/>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ItemsPresenter/>
</Border>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
DataTemplate For Brushes:
<DataTemplate DataType="{x:Type Brush}">
<Border BorderBrush="{Binding}" Margin="1" BorderThickness="1" Padding="2,3,2,3">
<TextBlock Text="{Binding}" TextAlignment="Center" Foreground="Black"/>
</Border>
</DataTemplate>
</Window.Resources>
Now, its usage:
<Grid>
<ItemsControl VerticalAlignment="Center" HorizontalAlignment="Center"
Style="{StaticResource ZoneItemsControlStyle}">
<SolidColorBrush Color="Red"/>
<SolidColorBrush Color="Green"/>
<SolidColorBrush Color="Black"/>
<SolidColorBrush Color="Blue"/>
</ItemsControl>
</Grid>
</Window>
Result:
See how I'm making use of DataTemplates in order to show a custom piece of UI for a specific Data Type? (In this case, System.Windows.Media.Brush class)
I'm "using the Brushes as ViewModels". You could also create your own ViewModels of course, and then create a specific DataTemplate for each VM type.
Also, see how I'm using the TemplateBinding MarkupExtension to bind several properties inside the ControlTemplate to the corresponding value in the ItemsControl instance.
Finally, see how you can actually add ANY kind of items to the ItemsControl.
Also, I must mention that I used this Style-based approach in order to enable reusability. You could place another ItemsControl somewhere else in the application and set its Style="{StaticResource ZoneItemsControlStyle}" and you're done. But if you only plan to use this a single time, you can just place all the properties hardcoded in the ItemsControl.Template ControlTemplate.
This is not a direct answer!
As #HighCore said, I tried to use an ItemsControl instead of implementing two ContentPresenters in my user-control. Just for clarity I made a new simple app to be able to describe it simply. So please consider some new assumptions:
Here again, there are two UserControls; MyItem and MyItemsControl as follows.
<UserControl x:Class="MyApp.MyItem"
∙∙∙>
<Grid x:Name="grdRoot">
<Border BorderBrush="{Binding ItemBorderBrsuh}" BorderThickness="1">
<TextBlock x:Name="txtColorIndicator"
Text="Item"
TextAlignment="Center"
Margin="5"/>
</Border>
</Grid>
</UserControl>
C# code-behind:
public partial class MyItem : UserControl
{
#region ________________________________________ ItemBorderBrsuh
public Brush ItemBorderBrsuh
{
get { return (Brush)GetValue(ItemBorderBrsuhProperty); }
set { SetValue(ItemBorderBrsuhProperty, value); }
}
public static readonly DependencyProperty ItemBorderBrsuhProperty =
DependencyProperty.Register("ItemBorderBrsuh",
typeof(Brush),
typeof(MyItem),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Black), FrameworkPropertyMetadataOptions.None, OnItemBorderBrsuhPropertyChanged));
private static void OnItemBorderBrsuhPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyItem instance = sender as MyItem;
if (instance != null && e.NewValue is SolidColorBrush)
instance.txtColorIndicator.Text = (e.NewValue as SolidColorBrush).Color.ToString();
}
#endregion
public MyItem()
{
InitializeComponent();
grdRoot.DataContext = this;
}
}
And this is the MyItemsControl.
<UserControl x:Class="MyApp.MyItemsControl"
∙∙∙>
<StackPanel>
<TextBlock x:Name="txtHeader" Margin="0,0,0,5" TextAlignment="Center" Text="0 Item(s)"/>
<Border BorderBrush="Gray" BorderThickness="1" Padding="5">
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyItem />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</StackPanel>
</UserControl>
C# Code-behind:
public partial class MyItemsControl : UserControl
{
private ObservableCollection<MyItem> _Items = new ObservableCollection<MyItem>();
public ObservableCollection<MyItem> Items
{
get
{
return _Items;
}
set
{
_Items = value;
}
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
txtHeader.Text = Items.Count + " Item(s)";
}
public MyItemsControl()
{
InitializeComponent();
Items.CollectionChanged += Items_CollectionChanged;
this.DataContext = this;
}
}
Here is how to use MyItem within MyItemsControl.
<Grid>
<local:MyItemsControl HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5" BorderBrush="Black" BorderThickness="1">
<local:MyItemsControl.Items>
<local:MyItem ItemBorderBrsuh="Green" Margin="1"/>
<local:MyItem ItemBorderBrsuh="Red" Margin="1"/>
<local:MyItem ItemBorderBrsuh="Blue" Margin="1"/>
<local:MyItem ItemBorderBrsuh="Orange" Margin="1"/>
</local:MyItemsControl.Items>
</local:MyItemsControl>
</Grid>
Now, there is no problem with BindingExpressions, but an important question remains. How to replace
{
grdRoot.DataContext = this;
}
and
{
this.DataContext = this;
}
with a true ViewModel?
Screenshot:
Edit: I tried to implement MVVM pattern but there are some problems. I asked the first one here.

Categories

Resources