|
Excellent control, very useful, worked for me without modification. Thanks very much.
|
|
|
|
|
|
Here's my scenario. I need a control with two "panes" that each bind to an IsVisible property. Both panes, only pane 1, only pane 2, or neither pane can be displayed. And I need the relative size of the panes to be adjustable (via a splitter drag action) by the user. The problem that I'm experiencing with the DockPanel control is that the DockPanel.Dock attached property doesn't have an explicit Fill state. And also the DockPanel has this weird behavior that even if the DockPanel only contains one non-collapsed UIElement (Visibility="Visible") and that element doesn't even have an explicitly set DockPanel.Dock attached property and the DockPanel's LastChildFill property is set to True, rather that expanding the non-collapsed UIElement to fill the DockPanel, it instead defaults to left-docking. Go figure! To be more clear, this behavior occurs when there exist additional collapsed elements beyond the one non-collapsed UIElement. Hence, when you collapse the second pane, the first pane doesn't expand to fill the DockPanel.
So I'm curious if there might be an easy way to tweak your control to achieve this behavior?
Many thanks!
|
|
|
|
|
Hi CDCii,
please create a small example application and we can see if we can get the dock panel splitter control to work correctly with it. The control is included in propertytools.codeplex.com, you can continue the discussion there. If the question is about DockPanel behaviour and not the DockPanelSplitter, a question on stack overflow may also be a good idea!
|
|
|
|
|
Painless and easy to implement. Does the job well.
|
|
|
|
|
any advice for creating support for this in silverlight?
|
|
|
|
|
Modified to run under silverlight, but it still need work.
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace Silverlight3dLib1
{
public partial class DockPanelSplitter2 : UserControl
{
//!//static DockPanelSplitter2()
//!//{
//!!//DefaultStyleKeyProperty.OverrideMetadata(typeof(DockPanelSplitter),
//!!// new FrameworkPropertyMetadata(typeof(DockPanelSplitter)));
// override the Background property
//!// BackgroundProperty.OverrideMetadata(typeof(DockPanelSplitter), new FrameworkPropertyMetadata(Brushes.Transparent));
// override the Dock property to get notifications when Dock is changed
//!// DockPanel.DockProperty.OverrideMetadata(typeof(DockPanelSplitter),
//!// new FrameworkPropertyMetadata(Dock.Left, new PropertyChangedCallback(DockChanged)));
//!//}
///
/// Resize the target element proportionally with the parent container
/// Set to false if you don't want the element to be resized when the parent is resized.
///
public bool ProportionalResize
{
get { return (bool)GetValue(ProportionalResizeProperty); }
set { SetValue(ProportionalResizeProperty, value); }
}
public static readonly DependencyProperty ProportionalResizeProperty =
DependencyProperty.Register("ProportionalResize", typeof(bool), typeof(DockPanelSplitter2),
new PropertyMetadata(true));
///
/// Height or width of splitter, depends of orientation of the splitter
///
public double Thickness
{
get { return (double)GetValue(ThicknessProperty); }
set { SetValue(ThicknessProperty, value); }
}
public static readonly DependencyProperty ThicknessProperty =
DependencyProperty.Register("Thickness", typeof(double), typeof(DockPanelSplitter2),
new PropertyMetadata(4.0, ThicknessChanged));
#region Private fields
private FrameworkElement element; // element to resize (target element)
private double width; // current desired width of the element, can be less than minwidth
private double height; // current desired height of the element, can be less than minheight
private double previousParentWidth; // current width of parent element, used for proportional resize
private double previousParentHeight; // current height of parent element, used for proportional resize
#endregion
public DockPanelSplitter2()
{
Loaded += DockPanelSplitterLoaded;
Unloaded += DockPanelSplitterUnloaded;
UpdateHeightOrWidth();
}
void DockPanelSplitterLoaded(object sender, RoutedEventArgs e)
{
Panel dp = Parent as Panel;
if (dp == null) return;
// Subscribe to the parent's size changed event
dp.SizeChanged += ParentSizeChanged;
// Store the current size of the parent DockPanel
previousParentWidth = dp.ActualWidth;
previousParentHeight = dp.ActualHeight;
// Find the target element
UpdateTargetElement();
}
void DockPanelSplitterUnloaded(object sender, RoutedEventArgs e)
{
Panel dp = Parent as Panel;
if (dp == null) return;
// Unsubscribe
dp.SizeChanged -= ParentSizeChanged;
}
private static void DockChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DockPanelSplitter2)d).UpdateHeightOrWidth();
}
private static void ThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DockPanelSplitter2)d).UpdateHeightOrWidth();
}
private void UpdateHeightOrWidth()
{
if (IsHorizontal)
{
Height = Thickness;
Width = double.NaN;
}
else
{
Width = Thickness;
Height = double.NaN;
}
}
public bool IsHorizontal
{
get
{
Dock dock = DockPanel.GetDock(this);
return dock == Dock.Top || dock == Dock.Bottom;
}
}
///
/// Update the target element (the element the DockPanelSplitter works on)
///
private void UpdateTargetElement()
{
Panel dp = Parent as Panel;
if (dp == null) return;
int i = dp.Children.IndexOf(this);
// The splitter cannot be the first child of the parent DockPanel
// The splitter works on the 'older' sibling
if (i > 0 && dp.Children.Count > 0)
{
element = dp.Children[i - 1] as FrameworkElement;
}
}
private void SetTargetWidth(double newWidth)
{
if (newWidth < element.MinWidth)
newWidth = element.MinWidth;
if (newWidth > element.MaxWidth)
newWidth = element.MaxWidth;
// todo - constrain the width of the element to the available client area
Panel dp = Parent as Panel;
Dock dock = DockPanel.GetDock(this);
//!// MatrixTransform t = element.TransformToAncestor(dp) as MatrixTransform;
//!// if (dock == Dock.Left && newWidth > dp.ActualWidth - t.Matrix.OffsetX - Thickness)
//!// newWidth = dp.ActualWidth - t.Matrix.OffsetX - Thickness;
element.Width = newWidth;
}
private void SetTargetHeight(double newHeight)
{
if (newHeight < element.MinHeight)
newHeight = element.MinHeight;
if (newHeight > element.MaxHeight)
newHeight = element.MaxHeight;
// todo - constrain the height of the element to the available client area
Panel dp = Parent as Panel;
Dock dock = DockPanel.GetDock(this);
//!//MatrixTransform t = element.TransformToAncestor(dp) as MatrixTransform;
//!//if (dock == Dock.Top && newHeight > dp.ActualHeight - t.Matrix.OffsetY - Thickness)
//!// newHeight = dp.ActualHeight - t.Matrix.OffsetY - Thickness;
element.Height = newHeight;
}
private void ParentSizeChanged(object sender, SizeChangedEventArgs e)
{
if (!ProportionalResize) return;
DockPanel dp = Parent as DockPanel;
if (dp == null) return;
double sx = dp.ActualWidth / previousParentWidth;
double sy = dp.ActualHeight / previousParentHeight;
if (!double.IsInfinity(sx))
SetTargetWidth(element.Width * sx);
if (!double.IsInfinity(sy))
SetTargetHeight(element.Height * sy);
previousParentWidth = dp.ActualWidth;
previousParentHeight = dp.ActualHeight;
}
double AdjustWidth(double dx, Dock dock)
{
if (dock == Dock.Right)
dx = -dx;
width += dx;
SetTargetWidth(width);
return dx;
}
double AdjustHeight(double dy, Dock dock)
{
if (dock == Dock.Bottom)
dy = -dy;
height += dy;
SetTargetHeight(height);
return dy;
}
Point StartDragPoint;
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
if (!IsEnabled) return;
Cursor = IsHorizontal ? Cursors.SizeNS : Cursors.SizeWE;
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (!IsEnabled) return;
if (!IsMouseCaptured)
{
StartDragPoint = e.GetPosition(Parent as UIElement);
UpdateTargetElement();
if (element != null)
{
width = element.ActualWidth;
height = element.ActualHeight;
IsMouseCaptured = true;
CaptureMouse();
}
}
base.OnMouseLeftButtonDown(e);
}
private Boolean IsMouseCaptured = false;
protected override void OnMouseMove(MouseEventArgs e)
{
if (IsMouseCaptured)
{
Point ptCurrent = e.GetPosition(Parent as UIElement);
Point delta = new Point(ptCurrent.X - StartDragPoint.X, ptCurrent.Y - StartDragPoint.Y);
Dock dock = DockPanel.GetDock(this);
if (IsHorizontal)
delta.Y = AdjustHeight(delta.Y, dock);
else
delta.X = AdjustWidth(delta.X, dock);
bool isBottomOrRight = (dock == Dock.Right || dock == Dock.Bottom);
// When docked to the bottom or right, the position has changed after adjusting the size
if (isBottomOrRight)
StartDragPoint = e.GetPosition(Parent as UIElement);
else
StartDragPoint = new Point(StartDragPoint.X + delta.X, StartDragPoint.Y + delta.Y);
}
base.OnMouseMove(e);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (IsMouseCaptured)
{
IsMouseCaptured = false;
ReleaseMouseCapture();
}
base.OnMouseLeftButtonUp(e);
}
}
}
|
|
|
|
|
Generally, while using grid-splitter we can restrict one splitter to move upto next/previous splitter. In other words, splitter moves in-between two rows/columns only. But here we can move splitter anywhere from left-to-right/top-to-bottom. Can we implement same restriction here?
|
|
|
|
|
When you resize the demo window down to a minimum and then up again, this exception is thrown:
System.ArgumentException was unhandled
Message="\"-4\" not valid for property \"Height\"."
Source="WindowsBase"
StackTrace:
bei System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, OperationType operationType, Boolean isInternal)
bei System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
bei System.Windows.FrameworkElement.set_Height(Double value)
bei OpenSourceControls.DockPanelSplitter.SetTargetHeight(Double newHeight) in DockPanelSplitterDemo\DockPanelSplitter\DockPanelSplitter.cs:Line 215.
|
|
|
|
|
thanks for the bug report! I cannot reproduce this in the demo application, but I understand there must be some problem in the SetTargetHeight/Width methods. I suggest you move the if statements just before the last line where the element's height is set. Please post back if this helps.
|
|
|
|
|
Hi,
I observe the same problem as kiol!
"Moving down the if statements" worked for me
Regards, H
Btw: Great work!!
|
|
|
|
|
I would like to use this cool tool in my code behind - but I can't get it to work.
It compiles and I can hit break points but I just can't "see" it.
If I use it in XAML, it works just fine.
Has anyone run into this or do I just suck
Thanks in advance,
Jason
|
|
|
|
|
OK, further testing - it is there. Just basically has zero width.
Mouse re-size cursor shows up and I can grab it and re-size the DockPanel.
Still stumped as to why it does not display using .cs file, but does putting it directly in XAML file.
|
|
|
|
|
hi Jason! the Thickness property should be used to change the width of the splitter. This should work both for horizontal and vertical splitters. Use the DockPanel.Dock property to set the orientation of the splitter. Let me know if there is a bug in the code, it seems to work here.
|
|
|
|
|
I did set both Thickness and DockPanel.Dock properties.
Works:
<br />
DockPanel dockPanel = (DockPanel) this.Parent;<br />
DockPanelSplitter dps = new DockPanelSplitter();<br />
<br />
<br />
dps.Thickness = 6;<br />
dps.BorderThickness = 6;
dps.Background = Brushes.AliceBlue;
dps.BorderBrush = Brushes.CornflowerBlue;
<br />
<br />
DockPanel.SetDock( dps, Dock.Left );<br />
<br />
dockPanel.Children.Add( dps );<br />
But this displays correctly in the XAML file:
<br />
...<br />
<DockPanel Background="White" Name="MainDockPanel" Grid.Row="1" Margin="0,2,0,-2"><br />
<br />
<TextBlock>Hello World</TextBlock><br />
<osc:DockPanelSplitter DockPanel.Dock="Left" Style="{StaticResource HorizontalBevelGrip}"/><br />
<br />
</DockPanel><br />
...<br />
|
|
|
|
|
Could you update your DockPanelSplitterDemo project to include a code behind example?
Please?
Thanks so much for building such a helpful widget!
Jason
|
|
|
|
|
Fixed:
Window window = Window.GetWindow( this );
DockPanelSplitter dps = new DockPanelSplitter();
dps.Style = (Style) ( window.Resources[ "VerticalBevelGrip" ] );
|
|
|
|
|
|
Thanks, objo, for writing this article: it's a really useful control and a nice demo to go with it.
I had one problem adding it to my own assembly so I thought I'd post the solution here for any other WPF-newbies using it.
I added the DLL to my own project and it worked but then I decided I needed to tweak it a bit so I added a Themes folder, putting generic.xaml into it and a Controls folder, putting DockPanelSplitter.cs into it. When I ran the app, the splitters didn't appear but there were no errors. I moved the mouse pointer over where the splitters should've been and it didn't change to the double-arrow. I discovered that I needed to add an attribute to AssemblyInfo.cs for it to work:
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
) ]
Once I'd added this code, it worked.
|
|
|
|
|
Thanks for the message - and showing your solution! Yes, this is a UserControl and if you don't reference the assembly, you need to copy the template (generic.xaml) and add the ThemeInfo.
|
|
|
|
|
Manually set the Background, BorderBrush and BorderThickness in Generic.xaml file and boom - it shows up.
Why does the TemplateBinding not work in code behind?
|
|
|
|
|
sorry for confusing with UserControl, it should of course be custom control.
|
|
|
|
|
|
thanks for the link! that's a good example of a user control! I like the adorner solution.
|
|
|
|
|
Failure by design:
You can move the splitter between green and red down out of the window and no chance to get it back. Will be hard to fix this.
|
|
|
|