AvalonDock使用心得「建议收藏」

发布时间:2025-12-09 13:44:07 浏览次数:3

  桌面程序的应用,不可避免的就会用到大量的布局控件,之前的一个项目也想过去做类似于Visual Studio的那种灵活的布局控件,也就是界面上的控件能够实现拖拽放置、隐藏、窗口化等一系列的操作,但由于开发时间以及需求的原因,没有太严格要求这方面功能的实现,也就只能算是想过一下而已,实际用的时候还是固定布局,但是最近接触到新的项目,需要这方面的应用就不得不自己动手查找和做这样的东西了。

  有朋友推荐RadControls里了控件——RadDocking,下载安装RadControls后,发现他里边的控件的确做的很不错,而且Demo也很详细,RadDocking也能满足我的需求,使用也还算方便,但是因为是试用版的,每次程序运行时都会出现相应的提示,尝试找他的最新版的激活成功教程版最终也无果,个人又不屑于用很久之前的版本,而且毕竟不是知根知底的东西,用起来也觉得怪怪的,所以还是放弃了使用RadDocking。

  就在我快要放弃寻找,准备有时间自己做的时候(后来发现自己想的有点简单了),让我发现了AvalonDock,看了下它的Demo发现效果也不错,至少看起来能够满足我的应用需求,而且还是开源的,顺便就当研究学习了。大家可以到http://avalondock.codeplex.com/下载和了解更加详细的信息。说了这么多废话赶紧进入正题吧!

  

  虽然有现成的Demo,但第一次接触这类控件还是折腾了不少时间,一点点的摸索它的使用方法!

  一、最基本的布局格式,容器的承载:



容器布局

      <
AvalonDock:DockingManager
x:Name
=”dockManager”
Grid.Row
=”2″
Margin
=”0,3,0,0″
>


<
AvalonDock:ResizingPanel
>


<
AvalonDock:ResizingPanel
Orientation
=”Vertical”
AvalonDock:ResizingPanel.ResizeWidth
=”0.2*”
>


<
AvalonDock:DockablePane
AvalonDock:ResizingPanel.ResizeWidth
=”0.1*”
>


<
AvalonDock:DockableContent
x:Name
=”CameraContent”
Title
=”摄像机”
FontFamily
=”微软雅黑”
FloatingWindowSize
=”250,300″
>


<
VideoMonitor:CameraControl
/>


</
AvalonDock:DockableContent
>


</
AvalonDock:DockablePane
>


<
AvalonDock:DockablePane
>


<
AvalonDock:DockableContent
x:Name
=”PTZControlContent”
Title
=”云台控制”
FontFamily
=”微软雅黑”
>


<
VideoMonitor:PTZControllerControl
/>


</
AvalonDock:DockableContent
>


</
AvalonDock:DockablePane
>


<
AvalonDock:DockablePane
AvalonDock:ResizingPanel.ResizeHeight
=”*”
>


<
AvalonDock:DockableContent
x:Name
=”PlayOperateContent”
Title
=”回放控制”
FontFamily
=”微软雅黑”
>


<
VideoMonitor:PlayOperateControl
/>


</
AvalonDock:DockableContent
>


</
AvalonDock:DockablePane
>


</
AvalonDock:ResizingPanel
>

  登陆以后在作了以下处理:


界面部分

<DataTemplate x:Key="LayoutNameListDataTemplate">
<RadioButton x:Name="radioButton" Margin="0,3,0,0" Content="{Binding Name, Mode=Default}" GroupName="LayoutNameList" VerticalContentAlignment="Top" Checked="layoutchose_Checked" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
 <Style x:Key="MoreWindowsListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<MenuItem Header="{Binding Name, Mode=Default}" IsCheckable="True" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=Default}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
 <
ListBox
ItemsSource
=”
{Binding LayoutList, ElementName=userControl, Mode=Default}

ItemTemplate
=”
{DynamicResource LayoutNameListDataTemplate}

/>

  也是通过绑定集合的方式与界面结合起来。由于布局列表在其他地方也用得到,还涉及到添加、删除等操作,为了保证界面与数据的实时响应,使用ObservableCollection<>集合来用于绑定。

content.StateChanged += new RoutedEventHandler(dokablecontent_StateChanged);
      content.Closed += new EventHandler(content_Closed);
content.Closing += new EventHandler<System.ComponentModel.CancelEventArgs>(content_Closing);
content.Closed += new EventHandler(content_Closed);
  以上几个事件尤其需要注意,鼠标对界面操作都是通过相应的事件来与列表之间相互响应的。同时空间的显示与隐藏的实现也在这段代码里。

  保存布局时我就采用的是让用户输入布局名称,根据该名称来保存布局。同时向布局列表中添加该项。  



保存布局

    
private

void
ok_Click(
object
sender, System.Windows.RoutedEventArgs e)
   {

MainWindow win
=
App.Current.MainWindow
as
MainWindow;
win.dockManager.SaveLayout(layoutname.Text
+


.xml

);
win.toolBar.LayoutList.Add(
new
SelectionItem() { Name
=
layoutname.Text });

this
.Close();
  }

  布局管理中,对已有的布局进行删除操作,删除列表中的项同时删除相应的文件。



删除布局

  
private

void
delect_Click(
object
sender, System.Windows.RoutedEventArgs e)
{


if
(layoutList.SelectedItems.Count
==

0
)
MessageBox.Show(

当前没有选中任何布局!

);

else

{

MessageBoxResult result
=
MessageBox.Show(

是否删除选中的布局?

,
“”
, MessageBoxButton.YesNo);


if
(result
==
MessageBoxResult.Yes)
{


while
(layoutList.SelectedItems.Count
!=

0
)
{

System.IO.File.Delete(layoutList.SelectedItems[
0
].ToString()
+


.xml

);
ToolBarControl tbcontrol
=

new
ToolBarControl();
tbcontrol.LayoutList.Remove((SelectionItem)layoutList.SelectedItems[
0
]);
}
}
}
}

  这样做可能可能不够完善,xml文件的查找就是一个问题,以后考虑通过读取xml文件内容来判断是否是布局文件,暂时还没有想到更好的办法,不知道大家有没有更好的经验呢?!

四、动态添加控件



添加控件

     private

void
ok_Click(
object
sender, System.Windows.RoutedEventArgs e)
{


if
(
!
GlobalMethod.TestString(videowinname.Text))
{

MessageBox.Show(

名称只能是字母和数字以及下划线,且不能以数字开头!

);
videowinname.Text
=

“”
;

return
;
}

VideoBroswerControl vbcontrol
=

new
VideoBroswerControl();
vbcontrol.LayoutChanged(VideoBroswerControl.layoutnum);

DocumentContent documentContent
=

new
DocumentContent()
{

Name
=
videowinname.Text,
Title
=
videowinname.Text,
Content
=
vbcontrol
};
MainWindow win
=
App.Current.MainWindow
as
MainWindow;
win.RegisterName(videowinname.Text, documentContent);
documentContent.Show(win.dockManager);

ToolBarControl tlcontrol
=

new
ToolBarControl();
SelectionItem item
=

new
SelectionItem()
{
Name
=
videowinname.Text ,
IsSelected
=
true

};
tlcontrol.DocumentContentList.Add(item);
documentContent.Closed
+=

new
EventHandler(tlcontrol.content_Closed);
documentContent.Closing
+=

new
EventHandler
<
System.ComponentModel.CancelEventArgs
>
(tlcontrol.content_Closing);

this
.Close();
}

五、其他

  还有这样一个事件时的注意的,其实我也说不好他的本质是什么,感觉好像就是每次启动新的布局时,如果以有布局存在空缺或已经关闭的情况下就会到达这里,所以在我在这里将缺失的控件给加上。



代码

       dockManager.DeserializationCallback 
+=
(s, e)
=>

{

DockingManager manager
=
s
as
DockingManager;

VideoBroswerControl vbcontrol
=

new
VideoBroswerControl();
vbcontrol.LayoutChanged(VideoBroswerControl.layoutnum);

var documentContent
=

new
DocumentContent()
{

Name
=
e.Name,
Title
=
e.Name,
Content
=
vbcontrol
};

e.Content
=
documentContent;
};

呵呵,一点小小经验,文章也拖了好久才写好,大家见笑啦!

偷懒了,没有写一个更好的Demo给大家,用了一些自己项目里现成的代码,希望有问题的朋友可以多多交流,也请大家多多指教,赐教我一些更好的方法!

转载于:https://www.cnblogs.com/wdysunflower/archive/2010/07/24/1779960.html

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477