ItemContainer style with nested ItemControls won't bind correctly - c#

A summary of what I'm trying to do:
I have a list of shapes
All shapes have a list of blocks
I want to draw all blocks in a canvas and bind their position.
But the problem right now is that even if it draw my blocks it don't bind the position correctly and they always show up at (0,0).
Here is how it look right now with a nested ItemsControl:
<ItemsControl ItemsSource="{Binding Path=Shapes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Aqua" Width="250" Height="400"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Blocks}">
<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>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
So what am I doing wrong or is it bad to do nested ItemControls?

Try using a RenderTransform instead of the Canvas Left and Top
<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>

Related

Retrieve button position relative to control

I have a grid that contains dynamically created buttons.
I have a selector tool that will select a "rectangle" of buttons and do whatever on them.
I'm stuck at this point, because I'm going down the MVVM route.. but I previously had a list of all the buttons and would do:
var buttonPos = button.TransformToAncestor(someGrid).Transform(new Point(0, 0));
Now I do not have the luxury of being able to get all the buttons in my itemscontrol to do that (or at least I don't know how).
This is a section of my XAML.
<Grid Name="mainGrid" DockPanel.Dock="Left" MouseDown="MainGrid_MouseDown" MouseUp="MainGrid_MouseUp" MouseMove="MainGrid_MouseMove" Background="Transparent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
MinWidth="{Binding GridWidth}" Height="{Binding GridHeight}">
<ItemsControl x:Name="ObjItemControl" ItemsSource="{Binding ObjCompositeCollection}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Name="objGrid" Grid.Row="1"
Width="{Binding MinWidth, ElementName=mainGrid}"
Height="{Binding Height, ElementName=mainGrid}"
engine:GridHelper.RowCount="{Binding RowCount}"
engine:GridHelper.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type engine:ObjA}">
<ToggleButton Content="{Binding Id}"
Tag="{Binding Id}"
IsChecked="{Binding IsSelected}"
Height="{Binding ElementName=ObjItemControl,
Path=DataContext.ButtonHeightWidth}"
Width="{Binding ElementName=ObjItemControl,
Path=DataContext.ButtonHeightWidth}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="{Binding ElementName=wellPlateItemControl,
Path=DataContext.ButtonMarginToUse}"
Padding="2"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown"
PreviewMouseLeftButtonUp="Button_PreviewMouseLeftButtonUp"
MouseEnter="Button_MouseEnter"
MouseLeave="Button_MouseLeave"
Style="{StaticResource ObjButton}">
</ToggleButton>
</DataTemplate>
<DataTemplate DataType="{x:Type engine:GridLabeller}">
<TextBlock Text="{Binding HeaderName}" Style="{StaticResource GridHeaders}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
<ItemsControl ItemsSource="{Binding RectForMarquee}">
<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}"
Visibility="{Binding Visibility}"
Stroke="{StaticResource ResourceKey=SecondaryThemeColour}"
StrokeThickness="2" StrokeDashArray="2,1"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--<Canvas>
<Rectangle x:Name="selectionBox" Visibility="Collapsed" Stroke="{StaticResource ResourceKey=SecondaryThemeColour}" StrokeThickness="2" StrokeDashArray="2,1"/>
</Canvas>-->
</Grid>
I want to be able to get all of the ObjA toggle buttons and do that TransformToAncestor check... unless there is a better way?
I can also do this in the View code-behind but not sure if this is a bit naughty...
private void MainGrid_MouseUp(object sender, MouseButtonEventArgs e)
{
// Need to find a better way for this...
List<UIElement> tgInItemControl = new List<UIElement>();
foreach (var toggleButton in VisualHelperExtensions.FindVisualChildren<ToggleButton>(ObjItemControl))
{
tgInItemControl.Add(toggleButton);
}
viewModel.MainGrid_MouseUp(tgInItemControl, e);
}

Add Rectangle and TextBlock to canvas dynamically using MVVM

I need help to populate canvas with rectangle and textblock
So far i am able to populate the canvas with rectangle using Mouse and using MVVM thanks to this post
https://stackoverflow.com/questions/22324359/add-n-rectangles-to-canvas-with-mvvm-in-wpf
But I need to add TextBlock for each drawn rectangle
this is what it should look like
Look alike of the application i want to create
This is my XAML code
<ItemsControl ItemsSource="{Binding RectItems, Source={x:Static VM:VMDrawing.instance}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas" Background="Transparent" Height="{Binding ElementName=image, Path=ActualHeight}" Width="{Binding ElementName=image, Path=ActualWidth}" >
<dxmvvm:Interaction.Behaviors>
<dxmvvm:EventToCommand EventName="MouseDown" Command="{Binding MouseDownCommand, Source={x:Static VM:VMDrawing.instance}}" PassEventArgsToCommand="True" EventArgsConverter="{StaticResource MDC}"/>
<dxmvvm:EventToCommand EventName="MouseMove" Command="{Binding MouseMoveCommand, Source={x:Static VM:VMDrawing.instance}}" PassEventArgsToCommand="True" EventArgsConverter="{StaticResource MMC}"/>
<dxmvvm:EventToCommand EventName="MouseUp" Command="{Binding MouseUpCommand, Source={x:Static VM:VMDrawing.instance}}" PassEventArgsToCommand="True" />
<dxmvvm:EventToCommand EventName="PreviewMouseLeftButtonDown" Command="{Binding PreviewMouseLeftButtonDownCommand, Source={x:Static VM:VMDrawing.instance}}" PassEventArgsToCommand="True" EventArgsConverter="{StaticResource PMLBDC}"/>
<dxmvvm:EventToCommand EventName="PreviewMouseLeftButtonUp" Command="{Binding PreviewMouseLeftButtonUpCommand, Source={x:Static VM:VMDrawing.instance}}" />
</dxmvvm:Interaction.Behaviors>
<Canvas.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleX="{Binding zoomValue, Source={x:Static VM:VMZoom.instance}}" ScaleY="{Binding zoomValue, Source={x:Static VM:VMZoom.instance}}"/>
</TransformGroup>
</Canvas.LayoutTransform>
</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>
<StackPanel>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Transparent" Stroke="Red" StrokeThickness="3" />
<TextBlock Text="Sample Text" Width="100" Height="100"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I tried adding
<StackPanel>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Transparent" Stroke="Red" StrokeThickness="3" />
<TextBlock Text="Sample Text" Width="100" Height="100"/>
</StackPanel>
Just to check if it will populate textblock for each rectangle. But it doesn't show any textblock. Does my understanding is wrong?
that this code
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
will set the position of each control in the canvas?
Update: It's working now. I forgot the fontsize of the TextBlock.
But how can i set the position of the TextBlock for each rectangle if the coordinates is from the
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
which I need in my Rectangle?
this is what my application look like now. But i need those textblock inside the rectangle not outside
enter image description here
Thank you so much
You are using a Stackpanel to display the Rectangle and the TextBlock... so the Rectangle and the TextBlock are stacked!
Use a Grid or a Canvas instead. Then you will be able to manage the position of your controls relatively to their container.
<Grid>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Transparent" Stroke="Red" StrokeThickness="3" />
<TextBlock Text="Sample Text" Width="100" Height="100"/>
</Grid>
<Canvas>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Transparent" Stroke="Red" StrokeThickness="3" />
<TextBlock Text="Sample Text" Width="100" Height="100" Canvas.Left="50" Canvas.Top="50"/>
</Canvas>

How to switch StackPanel items' Panel.ZIndex value with a Control outside?

Maybe this is a stupid asking, but I just wonder how to design this kind of layout:
first of all, I have a MenuList ObservableCollection. So I have to put these menus in to a stackPanel like this:
<Window.Resources>
<DataTemplate x:Key="MenuItemTemplate">
<Button Content="{Binding ContentText}" Panel.ZIndex="{Binding PIndex}" Padding="10" FontSize="20"/>
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Menus}" ItemTemplate="{StaticResource MenuItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
so it looks like this:
after a while, there's a new bar under this menu added in:
<ItemsControl ItemsSource="{Binding Menus}" ItemTemplate="{StaticResource MenuItemTemplate}">
.......
</ItemsControl>
<Border Background="Green" VerticalAlignment="Top" Panel.ZIndex="10" Height="30" Margin="0,81,0,0"/>
Now it's look like this:
The requirment is, if a button selected, it should on the top of the bar, other unselected buttons should be covered by the bar:
But the buttons' are in a StackPanel, so they're all based on StackPanel's ZIndex.
My question is, how to design such layout in xaml?
I think using the renderTransform is better. No need to hard code PIndex in xaml. See below.
<DataTemplate x:Key="MenuItemTemplate">
<Button Content="{Binding}" Padding="10" FontSize="20" Height="30" Width="100">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1" ScaleY="1.2"></ScaleTransform>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
<Border Background="Green" VerticalAlignment="Top" Height="30" Margin="0,150,0,0"/>
<ItemsControl ItemsSource="{Binding Menus}" ItemTemplate="{StaticResource MenuItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Code behind:
public ObservableCollection<string> Menus { get; set; }
EDIT:
<DataTemplate x:Key="MenuItemTemplate">
<Button Content="{StaticResource image1}" Height="30" Width="100">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Border x:Name="Border" Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}" Background="Orange">
</Border>
<ContentPresenter ContentSource="Content" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="RenderTransform" TargetName="Border">
<Setter.Value>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1" ScaleY="1.2"></ScaleTransform>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
If the bar is literally just a green bar, you can also put it on the DataTemplate as a separate Stackpanel from the button, and then put a Style.Trigger on the Button's stackpanel to increase the ZIndex OnMouseOver. The bar would look seamless even if it is just repeating.
So instead of:
<DataTemplate x:Key="MenuItemTemplate">
<Button Content="{Binding ContentText}" Panel.ZIndex="{Binding PIndex}" Padding="10" FontSize="20"/>
</DataTemplate>
You will have
<DataTemplate x:Key="MenuItemTemplate">
<Grid>
<StackPanel >
<StackPanel.Style>
<Style TargetType="StackPanel" >
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Panel.ZIndex" Value="3"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Panel.ZIndex" Value="1"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Button Width="100" Height="100" Content="{Binding ContentText}" Padding="10" FontSize="20" />
</StackPanel>
<StackPanel Panel.ZIndex="2">
<Border Background="Green" VerticalAlignment="Top" Height="30"/>
</StackPanel>
</Grid>
</DataTemplate>
(This doesn't look quite perfect if you run it, but you get the idea)
Dockpanel as our ItemPanelTemplate is the right choice.
Use a Grid with Button and Rectangle (for bottom border) as DataTemplate to show Menu items.
Don't apply margin in DataTemplate's Grid in step2 above. Instead use Grid Padding, Button margin for spacing.
DataTemplate :
<DataTemplate x:Key="MenuItemTemplate">
<Grid Background="DodgerBlue">
<Button Margin="5 5 5 0" HorizontalAlignment="Left" Panel.ZIndex="1" Content="{Binding ContentText}" Padding="1" Width="75" GotKeyboardFocus="Button_GotKeyboardFocus" LostKeyboardFocus="Button_LostKeyboardFocus"/>
<Rectangle Panel.ZIndex="2" Fill="BurlyWood" Height="5" Margin="0 25 0 0"/>
</Grid>
</DataTemplate>
ItemPanelTemplate :
<ItemsPanelTemplate>
<DockPanel Height="30" LastChildFill="True"/>
</ItemsPanelTemplate>
Use the below code as is :
<ItemsControl Panel.ZIndex="10" VerticalAlignment="Top" ItemsSource="{Binding Menus}" ItemTemplate="{DynamicResource MenuItemTemplate}" Margin="0">
<ItemsControl.Resources>
<DataTemplate x:Key="MenuItemTemplate">
<Grid Background="DodgerBlue">
<Button Margin="0" HorizontalAlignment="Left" Panel.ZIndex="1" Content="{Binding ContentText}" Padding="1" Width="75" GotKeyboardFocus="Button_GotKeyboardFocus" LostKeyboardFocus="Button_LostKeyboardFocus"/>
<Rectangle Panel.ZIndex="2" Fill="BurlyWood" Height="5" Margin="0 25 0 0"/>
</Grid>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel Height="30" LastChildFill="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Code-Behind
public IList Menus { get; set; }
public Window1()
{
InitializeComponent();
Menus = new[] { new { ContentText = "Menu1" }, new { ContentText = "Menu2" }, new { ContentText = "Menu3" } };
this.DataContext = this;
}
private void Button_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Panel.SetZIndex((Button)e.OriginalSource, 12);
}
private void Button_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Panel.SetZIndex((Button)e.OriginalSource, 1);
}

Using ItemsControl to bind buttons top/left position on canvas Windows Phone

I am using ItemsControl and have several buttons and place them at a specific location on the canvas. However, the Canvas.Left and Canvas.Top always place all buttons at 0,0.
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="{Binding Side}"
Height="{Binding Side}"
Background="{Binding BgColor}"
Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The best answer I've found for this is to use a RenderTransform in your datatemplate
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="{Binding Side}" Height="{Binding Side}" Background="{Binding BgColor}">
<Button.RenderTransform>
<TranslateTransform X={Binding Left} Y ={Binding Top}/>
</Button.RenderTransform>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Combobox selected item background at top not popup

I am looking for a way to set image background there.. Because it is white on a gray background.. Selected item trigger doesnt work because there is no parent ComboboxItem..
<ComboBox.Resources>
<DataTemplate x:Key="DataTemplate1">
<Border x:Name="border" Padding="10">
<Image Source="{Binding }" Stretch="None"/>
</Border>
</DataTemplate>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<WrapPanel Width="{Binding ActualWidth , RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ComboBox }}}" Background="{StaticResource ListItemSelected}" Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ComboBox.Resources>

Categories

Resources