I'm currently working on a .NET 4.7.1 application. I use a XAML WPF UI. I need to show rectangles on a canvas according to the values in a list in the bound view model.
I can show a static rectangle on my canvas, nevertheless I don't know how to show the rectangles from the ItemsControl DataTemplate.
Also, I need to use a certain style for my rectangles. I defined the style in the Canvas.Resources for now.
My current coding looks like this:
<Canvas x:Name="canvas" Grid.Row="2" ClipToBounds="True" Background="Gainsboro">
<Canvas.Resources>
<Style TargetType="Rectangle">
<Setter Property="Fill">
<Setter.Value>
<DrawingBrush TileMode="Tile"
Viewport="0,0,2,5" ViewportUnits="Absolute"
Viewbox="0,0,2,30" ViewboxUnits="Absolute">
<DrawingBrush.Transform>
<RotateTransform Angle="45"/>
</DrawingBrush.Transform>
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="Red" Thickness="10"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<LineGeometry StartPoint="0,15" EndPoint="30,15"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Setter.Value>
</Setter>
</Style>
</Canvas.Resources>
<!-- The static Rectangle works fine -->
<!--<Rectangle Width="100" Height="100" Canvas.Left="100" Canvas.Top="100"/>-->
<ItemsControl ItemsSource="{Binding RItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Canvas.Left="{Binding Y}" Canvas.Right="{Binding X}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Grid Width="{Binding ActualWidth, ElementName=canvas}" Height="{Binding ActualHeight, ElementName=canvas}">
<Label Content="Beginning" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left">
<Label.LayoutTransform>
<RotateTransform Angle="270"/>
</Label.LayoutTransform>
</Label>
<Label Content="End" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Right">
<Label.LayoutTransform>
<RotateTransform Angle="270"/>
</Label.LayoutTransform>
</Label>
</Grid>
</Canvas>
My basic RItems collection in the View Model looks like this:
public List<RItem> RItems { get; set; } = new List<RItem>
{
new RItem
{
X = 100,
Y = 100,
Width = 100,
Height = 100
}
};
Do you know what I'm doing wrong? How can I show my RItem from the View Model class on the Canvas?
Thanks a lot!!
The canvas should be inside the itemscontrol as it's itemspanel.
Something very roughly like:
<ItemsControl ItemsSource="{Binding RItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
.....
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Note also that each item goes inside a container and it is this which has to be positioned on the canvas.
I'm not sure why your grid is bound to the size of the canvas. Put the itemcontrol inside the grid and it'll fill the grid.
Related
I have grid on canvas background which can be seen in the picture. On the 1st row, I want to label the columns. I have used TextBlock for that purpose. But when the number of characters are increased I am unable to see the whole content of the TextBlock.e.g When the content is 9990, I can see it but in very next label the content is 10020 which is having more characters. I am able to see only 1002. The rectangle size is 30 which is drawing grid. TextBlock Width is 27 and Margin of TextBlock is 3.enter image description hereI do not want to change the font size.
<Canvas x:Name="back_canvas" Height="12000" Width="{Binding CanvasWidth , UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,10,0" >
<Canvas.Background>
<DrawingBrush TileMode="Tile" Viewport="0,0,30,30" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,30,30"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Gray" Thickness="1"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Canvas.Background>
<ItemsControl ItemsSource="{Binding TimeAxis}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Name="horizontalLabels" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Margin="0,0,3,0" Width="27" Background="Red" Height="Auto" >
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
As I already mentioned in a comment to the question you could set the TextTrimming of the TextBlock to CharacterEllipsis to indicate that some part is cut off and add a tooltip with the full string.
This would look something like this:
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" ToolTip="{Binding}" TextTrimming="CharacterEllipsis" Margin="0,0,3,0" Width="27" Background="Red" Height="Auto"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
This should change large numbers like 10020 from | 1002 | to something like | 100... | with a tooltip that says 10020
I want to bind a collection of multiple types to an ItemsControl being displayed in a canvas. My code so far is the following:
<ItemsControl ItemsSource="{Binding Path=Objects}">
<ItemsControl.Resources>
<DataTemplate DataType="self:CalibrationRectangle">
<Rectangle Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverter}}" Width="{Binding Width}" Height="{Binding Height}" />
</DataTemplate>
<DataTemplate DataType="self:PolygonVM">
<Polygon Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverter}}" Points="{Binding Points}" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Red" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--<ItemsControl.ItemTemplate>
<DataTemplate DataType="self:CalibrationRectangle">
<Rectangle Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverter}}" Width="{Binding Width}" Height="{Binding Height}" />
</DataTemplate>
</ItemsControl.ItemTemplate>-->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
In this state of the code the DataTemplates in the Resources are completely ignored and instead objects are represented as strings.
If instead I use the commented out lines and define the DataTemplate in ItemsControl.ItemsTemplate it works. However, as I need multiple DataTemplates (one per type) this is no solution.
What is the problem here? Why are the DataTemplates in the resources not working?
Any ideas are appreciated :)
At the moment you target XML element name. If you want to target type you need to use {x:Type ...} and replace
<DataTemplate DataType="self:CalibrationRectangle">
with
<DataTemplate DataType="{x:Type self:CalibrationRectangle}">
From MSDN:
To refer to the type name of the class, use the x:Type Markup Extension. If the template is intended for XML data, this property contains the XML element name.
I am trying to figure out how I can have a layer system for my app. I am adding images to an itemscontrol and they are all rendering one on top of the other.
<ItemsControl Name="canvasDataBinding"
HorizontalAlignment="Center"
Height="512"
Width="512"
VerticalAlignment="Center"
ClipToBounds="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Transparent"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding Name}"
Width="{Binding Width}"
Height="{Binding Height}">
</Image>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For testing purposes, I tried adding Panel.Zindex="{Binding Zind}" inside the image control, and decrementing Zind every time a new image was added to the collection, but the images still render on top of each other.
I also thought about having multiple collections, each collection would be its own layer. I dont know how I would implement this with my current code though.
Edit:
I added an ItemContainerStyle:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Panel.ZIndex" Value="{Binding Zindex}" />
</Style>
</ItemsControl.ItemContainerStyle>
Again I am binding the value of ZIndex to an int that I decrement before the image is added to the collection. It is still rendering one on top of the other.
You need to set your Canvas.Top, Canvas.Left, and Panel.ZIndex in the ItemContainerStyle so the property gets applied to the <ContentPresenter> that wraps each Image.
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Y}" />
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Panel.ZIndex" Value="{Binding Zindex}" />
</Style>
</ItemsControl.ItemContainerStyle>
When your ItemsControl gets rendered, it actually renders like this
<Canvas>
<ContentPresenter>
<Image />
</ContentPresenter>
<ContentPresenter>
<Image />
</ContentPresenter>
<ContentPresenter>
<Image />
</ContentPresenter>
</Canvas>
which is why it doesn't work to set the property on the <Image> itself.
I am designing a Windows 8.1 app in C#/XAML. I am trying to replicate the tilt effect found on Windows Phone. I have tried this with both a ScaleTransform and a PointerDownThemeAnimation.
However, the scale effect seems broken in the Y direction. It pushes the UI element down vertically on the page, instead of just scaling it smaller. I have used scale in Windows 8 apps before 8.1 and not had this issue.
Even stranger is in design view it works fine. I have tried setting RenderTransformOrigin to "0.5, 0.5". Here is an example of what happens when I set the text block's scale to 0.5:
What do you think the issue is and how can it be resolved? Thanks
Edit: Here is most of the XAML:
<Grid Height="{Binding PalettePanelHeight}">
<TextBlock Text="Palettes" FontSize="70" Margin="24,22,0,0"/>
<StackPanel Orientation="Horizontal" Name="typePanel" HorizontalAlignment="Right" Margin="0,52,24,0">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0,0,24,0"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="FontSize" Value="28"/>
</Style>
</StackPanel.Resources>
<TextBlock Text="Newest" Opacity="1" PointerPressed="on_PointerDown" PointerExited="on_PointerUp" PointerReleased="on_PointerUp" Tapped="newest_Tapped" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock Text="Most popular" Opacity="0.45" PointerPressed="on_PointerDown" PointerExited="on_PointerUp" PointerReleased="on_PointerUp" Tapped="popular_Tapped" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform/>
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock Text="Highest rated" Opacity="0.45" PointerPressed="on_PointerDown" PointerExited="on_PointerUp" PointerReleased="on_PointerUp" Tapped="rating_Tapped" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform/>
</TextBlock.RenderTransform>
</TextBlock>
</StackPanel>
The 'Newest' text block with scale 0.5 is shown in the example image. 'PalettePanelHeight' is just Window.Current.Bounds.Height in code.
If you want to keep the ScaleTransform, use VerticalAlignment to ensure that the text will be on top
<TextBlock VerticalAlignment="Top" Text="Newest" Opacity="1" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</TextBlock.RenderTransform>
</TextBlock>
If you want to keep it like other titles, remove the scaleTrandform and make it:-
<TextBlock VerticalAlignment="Top" Text="Newest" Opacity="1" RenderTransformOrigin="0.5,0.5">
</TextBlock>
I need to draw many rectangles(~50000). Currently I'm using the following approach.
<ItemsControl ItemsSource="{Binding Elements}" IsHitTestVisible="False" Background="Transparent">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"
Background="Transparent"
Width="500"
Height="500">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Bottom" Value="{Binding Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:Element}">
<Rectangle Width = "{Binding Width}"
Height = "{Binding Height}"
Fill= "{Binding Brush}"
SnapsToDevicePixels="True" >
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is that it takes a very long time to draw this amount of rectangles.
Is there a better way to draw a large amount of shapes?
Wpf has a pretty inefficient rendering engine, and 50000 shapes are way too much for it, even without the binding overhead.
Take a short look at this document: WPF rendering system
Instead, consider to use a drawing API such as Direct2D, which is compareably well accessible from WPF: Direct2D with WPF