I'm designing a spline editor which is composed of two parts (usercontrols)
The left control is the DesignerControl and the right one is the InfoControl. They share the same DataContext: DesignerVM with an
ObservableCollection<SplineVM> SplineVMList;
DesignerControl is an ItemsControl templated as a Canvas ("mycanvas") with ItemsSource bound to SplineVMList and ItemTemplate set as SplineControl.
InfoControl is a ListBox displaying the SplineVMs and a Grid displaying the SplineVMs' Properties.
In SplineControl I have a Canvas containing 4 draggable Points (Rectangle) and 2 Lines that will be bound to these Points. Everything works, I can drag my points, my lines move.
<UserControl>
<Canvas Width="300" Height="300" x:Name="mycanvas" Background="Transparent">
<Path x:Name="curve" Stroke="#F02828" StrokeThickness="3">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure>
<PathFigure.Segments>
<PathSegmentCollection x:Name="pathsegment"/>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
<Rectangle x:Name="curvePoint1" Width="10" Height="10" Canvas.Bottom="0" Canvas.Left="0" />
<Rectangle x:Name="curvePoint2" Width="10" Height="10" RadiusX="4" RadiusY="4" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(#VALUE/4)}"/>
<Rectangle x:Name="curvePoint3" Width="10" Height="10" RadiusX="4" RadiusY="4" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(#VALUE/2)}"/>
<Rectangle x:Name="curvepoint4" Width="10" Height="10" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(#VALUE)}"/>
<Line x:Name="line1" Stroke="#dfdfdf" StrokeThickness="1"/>
<Line x:Name="line2" Stroke="#dfdfdf" StrokeThickness="1"/>
</Canvas>
</UserControl>
My first problem is that I have to use a container (here a Canvas) to hold the path, rectangles and lines.
When i add a SplineControl in mycanvas, it's well placed and i can instantly drag my points but when i add another UserControl, it's placed above the previous one and i can't select the first Usercontrol's points.
I don't want to use IsHitTestVisible since I want to select points from the first userControl through the second.
My Second problem:
Since I use a Canvas to hold my things in the SplineControl, i can drag points outside of the canvas and still interact with it, and at first sight it was great.
But thinking it again, i'd like to resize my Canvas when i move a point to always have my point in the canvas. but also have my other points positions updated respecting the ratio of mycanvas.
Do you know any control that have this behavior and can replace my Canvas?
Should i use CustomControl instead of UserControl?
May I have to think again my project's conception?
Looking at your application details using Canvas makes sense to me. I have worked on a similar application and created a DesignerCanvas allowing items movement, resizing etc. and worked well.
First problem: I also faced similar problem and decided to add a delta to the position of my control's; so say location of first control is 0,0 then next control will be placed at 10,10; and next at 20,20; this way all the controls have visible area and are slectable(as soon as it's selected it comes to top).
Second problem: It's not a big problem to increase the width and height of Canvas when a control is dragged and making sure that control is not allowed to place outside Canvas. Will try to see if I can find the code related to that.
Look at this article series having similar implementation -
WPF Diagram Designer - Part 4, Part 3, Part 2, Part 1
In Fact I've found a way to get around my first problem of item selection through other items.
Canvas that were under the selected one were unclickable because the Color of every Canvas was set to Transparent. So I could see them but not interact with them.
Since I've set the Background property to Null (or haven't set it explicitly), I can click through and select the items under the top one.
It's a strange behavior of WPF...
Does someone have an explanation?
It will solve my 2nd problem too, 'cause I'll set my UserControl with the size I want and still select other UserControls in my View.
It's a dirty trick but I'll avoid the CustomPanel thing then.
thx anyway akjoshi and Danny Varod for your concern ^^
your answers were still useful since I'll use them in the other project I'm working on.
it's just that time is short on this one.
Related
I am writing a custom control for image display and interaction. Some of the features it needs is zooming and panning as well as drawing a custom region of interest, i.e. drawing a rectangle on top of the displayed frame with arbitrary position and size. Zooming and panning features are done and work nicely, I'm using a ScrollViewer, a scale transform and some event handlers for mouse clicking and moving in the C# code.
For the custom selection feature I want to add a canvas on top of the frame since that's the only way I know of that allows arbitrary positioning of a rectangle. In order for any shapes on the canvas to be mappable to image coordinates I need of course the canvas to exactly line up with the image so size and position must be the same. I also would like the image to keep adjusting to window resizes as it does without any canvas. Since Canvas doesn't automatically resize itself to anything I want to bind its dimensions to the size of the image like so (this is from the control template for the custom control):
<ScrollViewer x:Name="scrollViewer" Grid.Column="0" Grid.Row="1" CanContentScroll="True" PanningMode="Both" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<Grid>
<Image x:Name="screen" Panel.ZIndex="0" Cursor="Cross" Source="{TemplateBinding ColoredFrame}"/
<Canvas x:Name="testCanvas" Panel.ZIndex="1" Background="Transparent" Width="{Binding ActualWidth, ElementName=screen}" Height="{Binding ActualHeight, ElementName=screen}"/>
<Grid.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="{Binding RotationAngle, RelativeSource={RelativeSource TemplatedParent}}"/>
<ScaleTransform x:Name="scaleTransform" ScaleX="{Binding ZoomFactor, RelativeSource {RelativeSource TemplatedParent}}" ScaleY="{Binding ZoomFactor, RelativeSource={RelativeSource TemplatedParent}}"/>
</TransformGroup>
</Grid.LayoutTransform>
</Grid>
</ScrollViewer>
However, I am getting weird behaviour with this - when I use bindings for both Width and Height of the canvas to ActualWidth and ActualHeight of the image, like in the above code, it will automatically resize itself only when the size increases (when the user increases the size of the window) but not when it decreases. Any size increase is permanent and when decreasing the window again, one would have to scroll as the size of the image and canvas never decrease. This does not happen when I bind only one of the dimensions (width or height) and set the other one to a constant value. Binding modes (one way / two way) have no effect either.
I'm probably missing something really obvious but I've spent hours trying to figure this out and I am at a loss. Any help would be appreciated.
as far as I know Canvas does not have good sizing options. I think an Adorner will do what you try to achieve. For an example and additional information please have a look here: https://learn.microsoft.com/de-de/dotnet/framework/wpf/controls/adorners-overview
Happy coding
Tim
I'm trying to use the split button control described here:
http://mahapps.com/controls/split_dropdownbutton.html
I'm also using the MahApps resource pack with the icons collection. I want to re-size the control to a height of 40px, but the icon stays to its native height of 76px, resulting in it getting cropped, as shown below.
Any ideas how to get around this?
The SplitButton from MahApps.Metro has a property called IconTemplate. To this property you can assign a DataTemplate and there you can add almost arbitrary content, which will be put in place of the icon itself.
For example you could put a Rectangle control with fixed Height and/or Width. And setting its OpacityMask to your desired icon, will show the icon in smaller size.
In XAML it looks like this:
<controls:SplitButton Orientation="Horizontal">
<controls:SplitButton.IconTemplate>
<DataTemplate>
<Rectangle VerticalAlignment="Center" Height="35" Width="35" Fill="{DynamicResource BlackColorBrush}">
<Rectangle.OpacityMask>
<VisualBrush Visual="{StaticResource appbar_globe}" />
</Rectangle.OpacityMask>
</Rectangle>
</DataTemplate>
</controls:SplitButton.IconTemplate></controls:SplitButton>
controls is the alias for the MahApps.Metro-namespace and may differ in your project.
we have three control inside stackpannel now i want to change the control position dynamically .
we have searching Google but we don't find any solution.
please solve my problem.
thanx in advance.
If you are changing the position of elements inside a container then a StackPanel is the wrong container to use. It's designed to stack (!) elements on top of each other (or along side each other) with a spacing determined by the margins on each element.
If you want to move things around then you'll need a Canvas. With this you can then change the position of the elements on the canvas from code.
For example if you have the following:
<Canvas>
<Rectangle x:Name="Rectangle1" Canvas.Left="40" Canvas.Top="31"
Width="63" Height="41" Fill="Blue" />
<Ellipse x:Name="El;ipse1" Canvas.Left="130" Canvas.Top="79"
Width="58" Height="58" Fill="Blue" />
<Path x:Name="Path1" Canvas.Left="61" Canvas.Top="28"
Width="133" Height="98" Fill="Blue"
Stretch="Fill" Data="M61,125 L193,28"/>
</Canvas>
You can then change the position of elements like this:
Rectangle1.SetValue(Canvas.TopProperty, 45);
Rectangle1.SetValue(Canvas.LeftProperty, 60);
curious about using ellipse for showing progress, probably as the progress bar uses IsIndeterminate. I'll have ellipse where either color or opacity create an effect of moving
from left to right, but instead of a sold bar, ellipse controls spread apart. I'm not looking
for a working example, just hoping for some direction in the best way to pursue this.
<Canvas VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Name="canvas1" >
<Ellipse Height="15" Name="ellipse1" Fill="DodgerBlue" Stroke="DodgerBlue" Width="15" />
<Ellipse Height="15" Name="ellipse3" Fill="DimGray" Stroke="DodgerBlue" Width="15" Canvas.Left="21" Canvas.Top="0" />
<Ellipse Height="15" Name="ellipse2" Fill="DimGray" Stroke="DodgerBlue" Width="15" Canvas.Left="42" Canvas.Top="0" />
</Canvas>
I'd probably start by creating a UserControl that contains an animated ellipse that "pulses" -- becomes brighter and then fades back, waits a second, and then brightens again, and so on.
I'd put multiple instances of that control into a UniformGrid.
To get the desired effect of left-to-right, you'd want to expose some property that would be bound to the start time for the animation, so that the leftmost instance starts at 0, next slightly later, next slightly later, and so on. I'd expect to play with that timing until I get the desired left-to-right "flow".
Here's an example image of what I mean: example
The gray rectangle is the bounding box of a control that draws the blue lines and dots in it's OnRender(...) method. The red ovals mark places where it happens.
Why is that possible?
How can it be avoided?
Here's the perfect answer to my second question, at least when using a rectangular shaped control:
<object ClipToBounds="True" />
More details on the MSDN.
https://msdn.microsoft.com/en-us/library/ms750441(v=vs.100).aspx has detailed information about the architectural design of WPF to answer why it is possible.
To avoid it you want to use the clip property of your element.
<Rectangle Fill="Yellow" Height="100" Width="200" StrokeThickness="2" Stroke="Black">
<Rectangle.Clip>
<EllipseGeometry Center="200,100" RadiusX="50" RadiusY="50" />
</Rectangle.Clip>
</Rectangle>
Check out http://msdn.microsoft.com/en-us/library/cc189065%28v=VS.95%29.aspx for more details.