add icons to viewBox - c#

I have a Viewbox as ChessBoard
<Viewbox>
<ItemsControl Name="ChessBoard">
<ItemsControl.ItemsPanel >
<ItemsPanelTemplate >
<Canvas Width="8" Height="8" Background="{StaticResource Checkerboard}" MouseDown="Canvas_MouseDown" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Viewbox>
How i can add icon to the rectangles?

First of all... Your chess board is the ItemsControl, not the Viewbox. The Viewbox is simply containing the board for some special visualization woe we don't know (Maybe so you can use a Height of 8 without it appearing extremely tiny?).
Since you're using an ItemsControl, the correct way would be adding items to it and setting several DataTemplates, one for each type of piece. The "icon" would go inside each DataTemplate.
For instance, if your chess pieces are defined like this:
public class ChessPiece
{
public ColorEnum Color { get; set; }
public int X { get; set; }
public int Y { get; set; }
public virtual bool IsAValidMovement(int x, int y)
{
// base common logic
}
}
public class QueenPiece : ChessPiece
{
public override bool IsAValidMovement(int x, int y)
{
if (base.IsAValidMovement(x, y))
{
// specific logic
}
}
}
Then in your XAML you should do something like this:
<Viewbox>
<ItemsControl Name="ChessBoard">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="8" Height="8" Background="{StaticResource Checkerboard}" MouseDown="Canvas_MouseDown" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- You could also bind ItemsSource to a collection of chess pieces -->
<ItemsControl.Items>
<local:QueenPiece Color="Black" X="3" Y="0" />
</ItemsControl.Items>
</ItemsControl>
</Viewbox>
And in your Resources include a DataTemplate of the correct TargetType:
<Window.Resources>
<DataTemplate TargetType="{x:Type local:QueenPiece}">
<Grid Height="1" Width="1" />
<Image x:Name="Icon" Source="queen-white.png" Stretch="Uniform" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Color}" Value="Black">
<Setter TargetName="Icon" Property="Source" Value="queen-black.png" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
I'll leave positioning the pieces to you :P

A ItemControl is normally used to contain a collection of items.The ItemsPanelTemplate in the ItemControl usually describes how you want that collection of items to be displayed. These are normally generic types provided for by Microsoft. These include the WrapPanel and StackPanel. The item itself would be underneath the ItemTemplate. There you have the item template be customized to the viewmodel that is in the itemsource.
This should be how you look at it to start out.
<ItemsControl Name="ChessBoard" Width="50">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Black">
<Canvas Height="10"
Width="10"
Background="{Binding Color}">
</Canvas>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The setting of the items control is done in the code behind.
public MainWindow()
{
InitializeComponent();
List<stage> l = new List<stage>();
l.Add(new stage() { Color = "White" });
l.Add(new stage() { Color = "Black" });
l.Add(new stage() { Color = "White" });
l.Add(new stage() { Color = "Black" });
l.Add(new stage() { Color = "White" });
l.Add(new stage() { Color = "Black" });
l.Add(new stage() { Color = "White" });
l.Add(new stage() { Color = "Black" });
l.Add(new stage() { Color = "White" });
l.Add(new stage() { Color = "Black" });
ChessBoard.ItemsSource = l;
}
I need to let you know, this is not really a robust way of doing these thing. But this is the right direction you want to head in. But to just get an image in the canvas you will just need to add in an imagecontrol and bind to source.
<Canvas Height="10"
Width="10"
Background="{Binding Color}">
<Image Source="{Binding ImageSource}"/>
</Canvas>

Related

How to design the View layer when developing a Snake game using MVVM in WPF?

I am trying to develop a snake game in WPF using MVVM , https://github.com/Moore0/SnakeForWPF ,I had trouble designing the View layer
In the View layer I use ListBox as a container, ItemSource is binding to the background data source, set the ListBox's ItemsPanel For UniformGrid, set the DataTemplate to Border for each square, and bind the rows and columns of the board to UniformGrid.Rows and UniformGrid.Columns.
<ListBox x:Name="lv"
IsEnabled="False"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
BorderThickness="0"
Padding="0"
ItemContainerStyle="{StaticResource ListBoxForSnakePanelStyle}">
<ListBox.ItemsSource>
<!--Entity type design flaws, where multiple parameters must be passed in (but not important)...-->
<MultiBinding Converter="{converters:ItemSourceConverter}">
<Binding Path="SnakeNodes" />
<Binding Path="LineX" />
<Binding Path="LineY" />
<Binding Path="FoodPoint" />
</MultiBinding>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="1" Background="{Binding BlockType,Converter={converters:BlockBrushConverter}}">
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="{Binding LineY}" Columns="{Binding LineX}" Background="{StaticResource BackgroundLightBrush}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I think it's rather reluctant to do this. Is there a better way to design the View layer (which is limited to XAML as much as possible)
Here is a super quick implementation of the Canvas based solution I mentioned in comments. Turning this in to an actual workable game will require more work, of course, but it seems to handle the display very nicely.
The GameVm contains the viewmodel for the current game. It tracks the width and height of the board, as well as any "entities" currently in play. This includes the different snake-part segments, as well as the food pellets.
The XAML is basically an ItemsControl bound to the Entities property of the game. It selects the element's visual based on a template determined by the entity's type. I just used text characters, but you could easily replace these with images. The ItemsControl is hosted inside a Canvas, and the ContentPresenter of each item is styled to position it on the Canvas based on the entity's X and Y properties.
I would probably consider changing this to use column / row indexes, rather than "pixel" coordinates, with a ValueConverter to convert them to pixels for simplicity.
View models:
class MainWindowVm
{
public MainWindowVm()
{
Game = new GameVm(20, 20);
}
public GameVm Game { get; }
}
class GameVm : ViewModel
{
public GameVm(int width, int height)
{
Width = width;
Height = height;
Entities = new ObservableCollection<GameEntity>();
Entities.Add(new SnakeHead() { X = 20, Y = 20 });
Entities.Add(new SnakeBody() { X = 30, Y = 20 });
Entities.Add(new SnakeBody() { X = 40, Y = 20 });
Entities.Add(new SnakeTail() { X = 40, Y = 30 });
Entities.Add(new Food() { X = 0, Y = 0 });
Entities.Add(new Food() { X = 60, Y = 20 });
Entities.Add(new Food() { X = 50, Y = 50 });
Entities.Add(new Food() { X = 10, Y = 80 });
}
public ObservableCollection<GameEntity> Entities { get; }
private int _width;
public int Width
{
get => _width;
set => SetValue(ref _width, value);
}
private int _height;
public int Height
{
get => _height;
set => SetValue(ref _height, value);
}
}
abstract class GameEntity : ViewModel
{
private int _x;
public int X
{
get => _x;
set => SetValue(ref _x, value);
}
private int _y;
public int Y
{
get => _y;
set => SetValue(ref _y, value);
}
}
abstract class SnakeSegment : GameEntity { }
class SnakeBody : SnakeSegment { }
class SnakeHead : SnakeSegment { }
class SnakeTail : SnakeSegment { }
class Food : GameEntity { }
XAML:
<Window x:Class="Snake.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:Snake"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowVm />
</Window.DataContext>
<ItemsControl ItemsSource="{Binding Path=Game.Entities}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:SnakeHead}">
<TextBlock Text="%" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SnakeBody}">
<TextBlock Text="#" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SnakeTail}">
<TextBlock Text="." />
</DataTemplate>
<DataTemplate DataType="{x:Type local:Food}">
<TextBlock Text="O" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding Path=X}" />
<Setter Property="Canvas.Left" Value="{Binding Path=Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Window>
It produces the following output:

ItemsControl having multiple DataTemplate in WPF

I would like to draw in canvas different shapes. How can I make object from ArrowsItems ObservableCollection and CircleItems ObservableCollecton visible in canvas? I am also creating Shapes ObservableCollection including every Circle and Arrows Items. I think that propably the reason is in the data binding but don't know where.
The goal is possibility to generate and then draw programmatically circles and arrows.
<Button Grid.Row="1" MaxWidth="1000" Command="{Binding CreateEllipse}">Utwórz</Button>
<Viewbox Grid.Row="2" Margin="0 20 0 0" Stretch="Uniform" StretchDirection="Both" VerticalAlignment="Stretch">
<ItemsControl Name="Shape" ItemsSource="{Binding Shapes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="2000" Height="1200" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X, Mode=TwoWay}"/><Setter Property="Canvas.Top" Value="{Binding Y, Mode=TwoWay}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type core:CircleItem}">
<Viewbox Width="{Binding Width}" Height="{Binding Height}">
<!--MouseMove="Viewbox_MouseMove"-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{Binding DataContext.MyCommand, ElementName=Shape}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<local:DragBehavior/>
</i:Interaction.Behaviors>
<Grid>
<Grid.RenderTransform>
<TranslateTransform X="{Binding TransformX}" Y="{Binding TransformY}" />
</Grid.RenderTransform>
<Ellipse Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding Color}" />
<TextBlock HorizontalAlignment="Center" Text="{Binding Text}" TextAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Viewbox>
</DataTemplate>
<DataTemplate DataType="{x:Type core:ArrowItem}">
<Line X1="{Binding X1}" Y1="{Binding Y1}" X2="{Binding X2}" Y2="{Binding Y2}" Stroke="{Binding Color}" StrokeThickness="{Binding StrokeThickness}" />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Viewbox>
Also in my ViewModel:
public ObservableCollection<CircleItem> CircleItems { get; set; }
public ObservableCollection<ArrowItem> ArrowItems { get; set; }
public CompositeCollection Shapes { get; set; }
And after adding some objects of CircleItem class to CircleItems and ArrowItem to ArrowItems:
CompositeCollection coll = new CompositeCollection();
coll.Add(new CollectionContainer() { Collection = CircleItems });
coll.Add(new CollectionContainer() { Collection = ArrowItems });
Shapes = coll;
Make sure you initialize the Shapes property before the view model is assigned to the DataContext. The collection properties should all be readonly, otherwise you would have to fire a property change notification from their setters.
public class ViewModel
{
public ObservableCollection<CircleItem> CircleItems { get; }
= new ObservableCollection<CircleItem>();
public ObservableCollection<ArrowItem> ArrowItems { get; }
= new ObservableCollection<ArrowItem>();
public CompositeCollection Shapes { get; }
= new CompositeCollection();
public ViewModel()
{
Shapes.Add(new CollectionContainer { Collection = CircleItems });
Shapes.Add(new CollectionContainer { Collection = ArrowItems });
}
}
If I understood you correctly, you want to have different templates for different datatypes. This is quite easy to get. One of the ways (probably the simplest one):
create data templates for every of your types you want to show
create some kind of data template that will select the proper template:
<!-- Data template for arrows -->
<DataTemplate x:Key="ArrowsDataTemplate" DataType="{x:Type core:ArrowItem}">
<!-- Create here your template for arrows -->
</DataTemplate>
<!-- create data templates for other stuff and then "selector" data template -->
<DataTemplate x:Key="ContentDataTemplate">
<ContentPresenter x:Name="itemContentPresenter"
ContentTemplate="{StaticResource CircleDataTemplate}" <!-- just the default one -->
Content="{TemplateBinding Content}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding MyItemTypeAsEnum}" Value="Arrow">
<Setter TargetName="itemContentPresenter" Property="ContentTemplate" Value="{StaticResource ArrowsDataTemplate"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<!-- Now in your control (for example content control), where you show this stuff, do: -->
<ContentControl ContentTemplate="{StaticResource ContentDataTemplate}"/>
Now, I assumed that you have one base Item with property MyItemTypeAsEnum which will give you Circle for CircleItem, Arrow for ArrowItem etc. But if you don't have such property, you should be able to get boolean value from your viewmodel that will tell you if this item is Circle or not etc.
Into your main control, when you show thigs you have to set ContentTemplate to your "selector" template.

A master-detail TabControl binding (1:n) using parent SelectedItem with ICommand

I have XAML related question I have tried to research an answer in vain. I have commented the relevant questions to the XAML. It looks to me this questions is a more complex because of the way I try to arrange things.
Basically I have a main view model used in the TabControl headers and then in the content area I would show a list of items from the main view model. I just don't know how to to the binding. This is the main question. However, I suspect the next and ultimate objectives I have might factor in how to think about this, so I added them too. The rest of the code is for the sake of completeness.
<StackPanel>
<TabControl x:Name="mainsTabControl"
IsSynchronizedWithCurrentItem="True"
IsEnabled="True"
Visibility="Visible"
ItemsSource="{Binding Path=Mains}">
<!-- How to select a different background for the selected header? Note that the background color is "selected tab" if MainContentViewModel.IsActive is not TRUE.
If it is, a different color is chosen. Here this fact is just emulated with IsEnabled property due to well, multi-binding to the rescue (and a converter)? -->
<!-- Is there a clean way to use ICommand binding (RelayCommand) to check if it is OK to change the tab and if necessary, present a dialogue asking for the change? -->
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsEnabled" Value="{Binding IsActive}"/>
</Style>
</TabControl.ItemContainerStyle>
<!-- This binds to every item in the MainViewModel.Mains collection. Note the question about background color. -->
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<!-- This binding gives reference to the selected MainContentViewModel. -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="10" DataContext="{Binding ElementName=mainsTabControl, Path=SelectedItem, Mode=OneWay}">
<ItemsControl ItemsSource="{Binding Path=CustomItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</StackPanel>
using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;
using System.Linq;
namespace WpfDependencyInjection.ViewModel
{
public class MainContentViewModel: ViewModelBase
{
private ObservableCollection<CustomItemViewModel> customItems;
private mainContentDto MainContent { get; set; }
public string Name { get; }
public bool isActive;
public MainContentViewModel(Engine engine, mainContentDto mainContent)
{
MainContent = mainContent;
Name = MainContent.Name;
IsActive = true;
//The custom items belonging to this main content.
var customItems = engine.CustomItemContents.Where(i => i.MainContentId == MainContent.Id).Select(i => new CustomItemViewModel(engine, i));
CustomItems = new ObservableCollection<CustomItemViewModel>(customItems);
}
public ObservableCollection<CustomItemViewModel> CustomItems
{
get
{
return customItems;
}
set
{
customItems = value;
RaisePropertyChanged(nameof(CustomItems));
}
}
public bool IsActive
{
get
{
return isActive;
}
private set
{
isActive = value;
RaisePropertyChanged(nameof(IsActive));
}
}
}
}
public class CustomItemViewModel: ViewModelBase
{
private Engine Engine { get; }
private ItemTypeDto CustomItem { get; set; }
public string Name { get; }
public CustomItemViewModel(Engine engine, ItemTypeDto customItem)
{
Engine = engine;
CustomItem = customItem;
Name = customItem.Name;
}
}
namespace WpfDependencyInjection
{
public class Engine
{
public string Name { get; } = "EngineMan";
public List<mainContentDto> MainContents { get; set; } = new List<mainContentDto>(new[]
{
new mainContentDto { Name = "Main One", Id = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Version = 1 },
new mainContentDto { Name = "Main Two", Id = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E2"), Version = 1 }
});
public List<ItemTypeDto> CustomItemContents { get; set; } = new List<ItemTypeDto>(new ItemTypeDto[]
{
new ItemType1Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Name = "ItemType1Dto I", Id = Guid.NewGuid(), Version = 1 },
new ItemType2Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Name = "ItemType2Dto I", Id = Guid.NewGuid(), Version = 1 },
new ItemType2Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E2"), Name = "ItemType2Dto 2", Id = Guid.NewGuid(), Version = 1 }
});
public Engine()
{
}
}
}
<edit: The binding solved partially, though not the ICommand part.
Try this:
<TabControl x:Name="mainsTabControl"
IsEnabled="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Mains}"
SelectedItem="0"
Visibility="Visible">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter ContentSource="Header" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="HotPink" />
</Trigger>
</Style.Triggers>
</Style>
</TabControl.ItemContainerStyle>
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:MainContentViewModel}">
<Button Background="{x:Null}"
Command="{Binding SomeCommand}"
Content="{Binding Name}"
FocusVisualStyle="{x:Null}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type local:MainContentViewModel}">
<ItemsControl Margin="10"
VerticalAlignment="Top"
ItemsSource="{Binding Path=CustomItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:CustomItem}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Command:
There may be cleaner ways but here we bind IsSelected for the TabItem to the IsSelected property of the viewmodel. This enables having a command that asks if it is ok to navigate and sets IsSelected to true if it is.
Background:
We also retemplate the tabitem so that background works as we want. If you check with Snoop WPF inserts an extra border when the item is selected.
Side note 1:
Don't put the TabControl into a StackPanel like that. A StackPanel sizes to content and will kill scrolling and draw outside the control. Also it comes with a cost, a deep visual tree is not cheap. Same in the ItemTemplate and the other places. In fact StackPanel is rarely right for anything :)
Side note 2:
If you specify DataType in your DataTemplate you get intellisense and some compiletime checking.

WPF copying controls programmatically

I created an example control that I want to copy as many times as many I will set in code. I want to duplicate entire <ToggleButton> control.
XAML:
<WrapPanel Name="varom">
<ToggleButton Margin="10">
<StackPanel Orientation="Horizontal">
<Label Content="Stop sign" />
<Image Width="16" Source="{Binding appbar_stop}" />
</StackPanel>
</ToggleButton>
<ToggleButton Margin="10">
<StackPanel Orientation="Horizontal">
<Label Content="Stop sign" />
<Image Width="16" Source="{Binding appbar_stop}" />
</StackPanel>
</ToggleButton>
</WrapPanel>
Now I copied one time <ToggleButton> manually, but if I would have just one <ToggleButton> and I want to get second without copying xaml code...
Is it possible to duplicate(copy) <ToggleButton> control using code?
C#:
namespace WpfApplication3
{
public partial class MainWindow : MetroWindow
{
public const int maxButtons = 4; // number of copies for example
public MainWindow()
{
InitializeComponent();
// code add here for example :)
}
}
}
Models:
public class ButtonViewModel
{
public string Caption { get; set; }
}
public class ViewModel
{
public ViewModel()
{
Buttons = new ObservableCollection<ButtonViewModel>
{
new ButtonViewModel { Caption = "Button 1" },
new ButtonViewModel { Caption = "Button 2" },
new ButtonViewModel { Caption = "Button 3" },
};
}
public ObservableCollection<ButtonViewModel> Buttons { get; }
}
XAML:
<ItemsControl ItemsSource="{Binding Buttons}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Margin="10">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Caption}" />
<Image Width="16"/>
</StackPanel>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Add n rectangles to canvas with MVVM in WPF

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...

Categories

Resources