How to set proportional Canvas.Left? - wpf

Is there the way to set the distance between the left side of a control and the left side of its parent Canvas proportionally, not as a fixed value?
I use Canvas (actually DragCanvas, because I allow for the user to drag controls in the canvas). I'm binding values in xaml:
<ItemsControl ItemsSource="{Binding Tables}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{DynamicResource TableButton}" Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=YTransl}" />
<Setter Property="Canvas.Left" Value="{Binding Path=XTransl}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
<j:DragCanvas Margin="10"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The problem that the controls which are inside of canvas for bigger screen size, are outside of view for smaller screen size.
I need to set the value "YTransl" and "XTransl" as percentage of the whole canvas, proportionally to the screen size.
Something like <RowDefinition Height="1*" /> in Grid container.
Or multiplying by screen width and height... I don't know if it is good idea..
Is there the way to do it in xaml? Or I should calculate this in cs and bind the prepared value in xaml?

Related

How to make scrollbar visible around a Canvas in WPF

I have a list of items, which I display on a Canvas. I display those items vertically, the next below the previous, just like in ListBox or ListView.
Because I need to be able to drag those items around and for that purpose I have to use a Canvas, since Canvas allows me to set concrete positions for the items.
There are many items in the collection reaching outside the boundaries of the window when displayed and for that I need to use a ScrollViewer. The problem here is that if I don't set the Canvas's height, then the ScrollViewer isn't visible.
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="500" /> // Can't set Height here
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
What could I do to make the vertical scroll bar around canvas visible?
So, based on #EdPlunkett's answer I came up with this solution:
I added bindings to MinHeight and MinWidth of the ItemsControl (altought the normal Height and Width would work as well I suppose).
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding AlbumItems}"
MinHeight="{Binding EditorHeight}"
MinWidth="{Binding EditorWidth}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
and in my data context I call this method to calculate the size and set the EditorHeight and EditorWidth properties to their calculated values.
private void calculateEditorSize()
{
int maxEditorHeight = AlbumItems.Max((component) => component.Top + component.Height);
int maxEditorWidth = AlbumItems.Max((component) => component.Left + component.Width);
EditorHeight = maxEditorHeight;
EditorWidth = maxEditorWidth;
}

Using a ScrollViewer with an ItemsControl with a Canvas as the ItemsPanel

I have an ItemsControl and I set the ItemsPanel to a Canvas. The Canvas needs to be able to dynamically size with the content I put in it, and I need to be able to scroll when content runs off the bounds of the control. The trouble is I can't get the content to scroll. I have the scroll bar visibilities set to auto, so I don't end up seeing the scroll bars pop up when content runs off the edge.
I tried both putting the ItemsControl inside a ScrollViewer, and I tried using a ScrollViewer in the ItemsControl's Template.
Here's the ItemsControl inside the ScrollViewer:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplateSelector>
...
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
</ScrollViewer>
And here it is with the ScrollViewer in the Template:
<ItemsControl ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplateSelector>
...
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
This post on MSDN seemed promising, but implementing it on my code, or even explicitly with a Canvas in lieu of a WrapPanel wasn't working (or, should I say, I wasn't able to get it to work).
I've also taken a look at this post but the solution doesn't work for me as I need the canvas to be able to size with the content (otherwise the scroll bars are always visible).
Thank you kindly, in advance!
The canvas's width/height when not explicitly set, will inherit from the ItemsControl. The "dynamic sizing" behavior you're expecting isn't how panels sizing and layout works out of the box.
You options are to:
Explicitly set the ItemsControl Width/Height.
Explicitly set the Canvas Width/Height.

Dragging behavior is not working for items in ItemsControl: MouseDragElementBehavior

I have a class
public class DrawingCanvas : FrameworkElement
which uses MouseDragElementBehavior to implement dragging.
I want to drag the DrawingCanva like a layer in Photoshop relative to another layer
Working alone with (XAML on pic #1)
<Grid x:Name="_layoutRootControl">
<Canvas
Grid.Column="0">
<canvas:DrawingCanvas />
<canvas:DrawingCanvas />
</Canvas>
</Grid>
do allows dragging, but when I place DrawingCanvas inside ItemsControl
and bind ItemsSource to collection of items, draggins doesn't work.
(XAML on pic #2)
<ItemsControl ItemsSource="{Binding Source={StaticResource Locator}, Path=LayersViewModel.Layers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="0" />
<Setter Property="Canvas.Top" Value="0" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<canvas:DrawingCanvas />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I don't understand why.
First case is the following XAML structure:
And the second case is the following XAML structure:
Update:
I've found out that in second case with ItemsControl a DrawingCanvas.Parent = null
In connection with that item.Parent inside ItemsControls is always null
I've found the solution in the following topics with explanation:
Why does the Parent property of a container of an ItemsControl return null and not the Panel it sits on?
Combining ItemsControl with draggable items - Element.parent always null
Update:
Now the Parent is not null, still the problem remains.
Update 2
The last lacking thing
I had to define "extenders:DragHelper.CanDrag" inside ContentPresenter instead of
<ItemsControl.ItemTemplate>
The resulting template is as follows, so the issue is solved:
<ItemsControl
ItemsSource="{Binding Source={StaticResource Locator}, Path=LayersViewModel.Layers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property=**"extenders:DragHelper.CanDrag"** Value="True"/>
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<surfaces:DrawingCanvas />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Issue when setting a Style for StackPanel elements in an ItemsControl

If I create a simple StackPanel and place some items in it and define a style to add margins, all works well, as demonstrated by this quick example:
<StackPanel Grid.Row="1" Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="module:ModuleElement">
<Setter Property="Margin" Value="20"/>
</Style>
</StackPanel.Resources>
<module:ModuleElement/>
<module:ModuleElement/>
</StackPanel>
Obviously module:ModuleElement is my UserControl, but the issue remains if I use a default control, like a typical Button.
However, I don't want just a StackPanel - I want an ItemsControl. So I defined my control as such:
<ItemsControl Grid.Row="1" Margin="20,0,20,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="module:ModuleElement">
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Items>
<module:ModuleElement/>
<module:ModuleElement/>
</ItemsControl.Items>
</ItemsControl>
The few items added in the code above are for testing purposes - in this case the margins are default values (0).
What's going on? Why isn't this working? What do I need to do to fix this? I could of course set the margins manually for each element, but I feel I missing something and would much rather see it defined in a Style...
You might put the Style into the ItemsControl Resources instead of the StackPanel Resources:
<ItemsControl Grid.Row="1" Margin="20,0,20,0">
<ItemsControl.Resources>
<Style TargetType="module:ModuleElement">
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Items>
<module:ModuleElement/>
<module:ModuleElement/>
</ItemsControl.Items>
</ItemsControl>
Resource lookup in Silverlight traverses the "object tree" (see here) which as far as I understand is not the visual tree, but the tree defined in XAML.

sort of special accordion like control

I have put an example link in here:
http://activeden.net/item/xml-horizontal-vertical-accordion-banner-rotator/full_screen_preview/127714?ref=premiumtemplates
I try to achieve something similar (but far more basic) with WPF.
Not the flying text stuff, only the basic navigation idea.
I tried to build it with some expander controls and a stackpanel.
What I came up with is this:
<ItemsControl Grid.Row="1" IsTabStop="False" ItemsSource="{Binding Path=tabs,Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
I use MVVM, so there is also a template which is applied:
<DataTemplate DataType="{x:Type vm:TabulatorViewModel}">
<Expander HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ExpandDirection="{Binding .direction,Mode=OneWay}" IsExpanded="{Binding .isExpanded,Mode=TwoWay}" Header="{Binding .header,Mode=OneWay}" >
<Expander.Style>
<Style TargetType="{x:Type Control}">
<Setter Property="Template" Value="{StaticResource HorizontalExpanderRight}" />
<Style.Triggers>
<DataTrigger Binding="{Binding .direction}" Value="Left">
<Setter Property="Template" Value="{StaticResource HorizontalExpanderLeft}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<StackPanel>
<Label Content="{Binding .seitenInhalt.Header,Mode=OneWay}"></Label>
<TextBox Text="{Binding .seitenInhalt.Inhalt,Mode=OneWay}"></TextBox>
<Button Content="zurück" Command="{Binding .seitenInhalt.MovePreviousCommand}" />
<Button Content="vor" Command="{Binding .seitenInhalt.MoveNextCommand}"/>
</StackPanel>
</Expander>
</DataTemplate>
So, this is working, at least kind of.
two screenshots from my current project to explain the issues:
Could not post picture because of reputation points.
All Items together should use the complete width of the stackpanel, not like in the picture. Could not post picture because of reputation points.
All items should use the complete width, but the one expanded item should have a bigger width then the rest. As on the picture, but the collapsed items should use the remaining space, each by the same amount filling the gap)
Any help would be great, I hope it is possible to understand my goal / issues.
I think what you want to use is a UniformGridPanel or WrapPanel, which will use the full width available, rather than a StackPanel which is made to use the minimum width possible. The panel overview is here.
You'll probably want to use a Grid with the Height/Width of the Columns/Rows set to * if the item is Expanded, or Auto if not.
Also, if you're using a Grid in an ItemsControl, you need to set the Grid.Row/Grid.Column, and Width/Height properties on the ContentPresenter, not the ItemTemplate since the ItemTemplate in a ItemsControl is always wrapped in a ContentPresenter.

Resources