I need to draw a rectangle with size of 10x10cm using WPF (Canvas). I tried many ways to convert centimeters to pixels. I used the LengthConverter class, I used an explicit assignment (Width="10cm") but the actual width and height of rectangle are less than 10cm (about 9.35cm). It is a bit strange.
As I know, 1 WPF unit == 1/96 inch by default. Where could be a problem?
Here is a piece of my XAML:
<ItemsControl Grid.Row="1" ItemsSource="{Binding Cells}" Background="White"
ClipToBounds="True">
<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="10cm" Height="10cm" StrokeThickness="0" Fill="Black"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Related
I'm new on WPF and I'm tryng to figure out how to implement a binding with different type of user control.
After the user clicks a button, a usercontrol (a simple shape like rectangle or ellipse) is added to the window.
I'm tryng to use an MVVM approach so the xaml appears as follow:
...
Title="{Binding Path=Titolo}"
Height="450" Width="800"
d:DataContext="{d:DesignInstance vm:MainWindowViewModel}">
<Canvas>
<Button Content="Add Shape" Command="{Binding TestCommand}"/>
<ItemsControl ItemsSource="{Binding RectCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomRectangle/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
All works fine but I want to add different type of UserControl (not only MyCustomRectangle) using the same button (for example, randomly adding rectangle or ellipse).
A possible solution could be duplicate the section of ItemsControl and select a different collection of binding:
<Canvas>
<Button Content="Add Shape" Command="{Binding TestCommand}"/>
<ItemsControl ItemsSource="{Binding RectCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomRectangle/> <!-- bind to my usercontrol -->
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
<ItemsControl ItemsSource="{Binding EllipseCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomEllipse/> <!-- bind to my usercontrol -->
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
I don't think this is the correct solution, especially because I would to add many different types of shapes (and also text and images).
So, if exist, what is the correct way to bind a data template to different type of usercontrol?
Is MVVM the correct approach to solve this problem?
What you can do is to bind to a global list with all Shapes.
And then you can define different DataTemplates for different Types.
Like this:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type MyType1}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type MyType2}">
....
</DataTemplate>
</ItemsControl.Resources>
I have a block of code in my xaml that it is repeated several times, each to draw a different predefined figure (rectangle 1,2...n, sellipse1,2...n, etc) programmatically:
<ItemsControl ItemsSource="{Binding MyEllipse}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="200" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</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>
<Ellipse Width="{Binding Width}" Height="{Binding Height}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I would like to find a way of having this summarized with a Data/Item TemplateSelector, but after trying several approaches i couldn't get it to work.
Any little working example would be much appreciated, thanks!
I created a .NET WPF Application with MvvmLight this evening. I'm trying to show a couple of circles on my main window.
My MainWindow.xaml is bound to MainViewModel.cs. MainViewModel contains an ObservableCollection with SpaceObjects. I'm trying to show this collection using itemcontrols.
<ItemsControl ItemsSource="{Binding SpaceObjects}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding }"></Setter>
<Setter Property="Canvas.Top" Value="{Binding Radius}"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="{Binding Radius}" Height="{Binding Radius}" Fill="Blue" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In the ItemTemplate I am able to bind to a SpaceObject property (like Radius). In ItemContainerStyle I can only choose from the context:
I would like to choose from the items in the source (ItemsScourse SpaceObjects), so I can bind the X and Y coordinates from the circle. What am I doing wrong?
<DataGrid.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<d:Style.DataContext>
<x:Type Type="SpaceObject" />
</d:Style.DataContext>
<Setter Property="Canvas.Left" Value="{Binding }"/>
<Setter Property="Canvas.Top" Value="{Binding Radius}"/>
</Style>
</DataGrid.ItemContainerStyle>
I have a task to draw multiple rectangles and manipulate them. So I'm using an ItemsControl with it's ItemsPanel as a Canvas, and a wrapping ScrollViewer:
<ScrollViewer Height="200" Grid.Row="1" >
<ItemsControl Name="rectanglesList" Background="AliceBlue">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Y}"/>
<Setter Property="Canvas.Top" Value="{Binding X}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Black">
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding Color}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Now since the Canvas height and width properties do not include it's children, the Scrollviewer won't work unless I change the ItemsControl height and width manually by calculating summing the heights and widths of the children and assigning them to the ItemsControl.
The question is now is there any property 'm missing that does this automatically?
You may use a custom Canvas that overrides the MeasureOverride method
public class MyCanvas : Canvas
{
protected override Size MeasureOverride(Size constraint)
{
base.MeasureOverride(constraint);
var size = new Size();
foreach (var child in Children.OfType<FrameworkElement>())
{
var x = GetLeft(child) + child.Width;
var y = GetTop(child) + child.Height;
if (!double.IsNaN(x) && size.Width < x)
{
size.Width = x;
}
if (!double.IsNaN(y) && size.Height < y)
{
size.Height = y;
}
}
return size;
}
}
and which would be used like this:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:MyCanvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
It requires that the item container element, besides Canvas.Left and Canvas.Top has its Width and Height set in the ItemContainerStyle:
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
<Setter Property="Width" Value="{Binding Width}"/>
<Setter Property="Height" Value="{Binding Height}"/>
</Style>
</ItemsControl.ItemContainerStyle>
The ItemTemplate would then just look like this:
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Black">
<Rectangle Fill="{Binding Color}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
or
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle StrokeThickness="1" Stroke="Black" Fill="{Binding Color}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
You may also want to put the SCrollViewer into the ControlTemplate of the ItemsControl:
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
#Clemens's answer is great. I just wanted to add some context as to why it's necessary in case it wasn't clear.
In WPF the Canvas panel does not consider its children when asked to measure itself. It is the responsibility of the parent control to ensure the canvas is sized appropriately for the items it will contain.
In the subclass #Clemens provides, the logic for considering all children is provided in MeasureOverride.
Note that this will not consider any non-FrameworkElement children. If you add a very large number of children, you may notice a performance hit during layout.
Can anyone explain what's going on here. The two news articles in this WrapGrid aren't taking up the full space available to them as visible in the image below
My xaml looks like this
<HubSection Header="{Binding SportArticles[0].Title}" Background="{Binding SportArticles[0].Image}" MaxWidth="1000" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">
<DataTemplate>
<ItemsControl
x:Name="xItems" ItemTemplate="{StaticResource ScoreNewsArticleTemplate}" ItemsSource="{Binding SportArticles}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" ItemWidth="350"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</HubSection>
The outer blue box is the HubSection and the inner blue box is the ItemsControl. I can't understand why its not taking up the full space available
Try setting the VerticalContentAlignment of your ItemContainerStyle to Stretch.
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ItemsControl.ItemContainerStyle>