WPF:从WPF Diagram Designer Part 2学习面板、缩略图、框线选择和工具箱
发布日期:2021-10-24 03:36:33 浏览次数:1 分类:技术文章

本文共 15881 字,大约阅读时间需要 52 分钟。

  在中介绍了图形设计器的移动、大小和旋转等功能的实现,本篇继续第二部分,学习设计面板、缩略图、框线旋转和工具箱等功能的实现。

2009112317371418.png

设计面板(Designer Canvas :variable size, scrollable)

  在中的示例出来的设计器,当把设计对象拖动到DesignerCanvas边界外时,因为DesignerCanvas没有滚动条,我们会发现再也找不到这个对象了。想到解决最简单的办法就是给DesignerCanvas添加一个ScrollViewer,但是这个办法解决不了这个问题,因为当拖动到Canvas之外时,并不会出发Canvas的大小发生变化,所以仍旧没有滚动条,为了解决这个问题,我们则必须在设计对象移动和改变大小时去调整Canvas的大小。

  WPF控件提供一个MeassureOverride允许控件计算希望的大小,再返回WPF框架来进行布局。我们可以在DesignerCanvas中重载这个方法来解决上面所说的问题,重载方法如下:

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
protected
override
Size MeasureOverride(Size constraint)
{
Size size
=
new
Size();
foreach
(UIElement element
in
base
.Children)
{
double
left
=
Canvas.GetLeft(element);
double
top
=
Canvas.GetTop(element);
left
=
double
.IsNaN(left)
?
0
: left;
top
=
double
.IsNaN(top)
?
0
: top;
//
measure desired size for each child
element.Measure(constraint);
Size desiredSize
=
element.DesiredSize;
if
(
!
double
.IsNaN(desiredSize.Width)
&&
!
double
.IsNaN(desiredSize.Height))
{
size.Width
=
Math.Max(size.Width, left
+
desiredSize.Width);
size.Height
=
Math.Max(size.Height, top
+
desiredSize.Height);
}
}
//
for aesthetic reasons add extra points
size.Width
+=
10
;
size.Height
+=
10
;
return
size;
}

  注:当设计对象很多时,我猜测可能会有性能问题。在介绍了一个可以显示百万级对象的示例,不知道能否解决这个性能问题,先把这个在这里留个足迹,以便以后可以找到

缩略图(Zoombox)

2010081714102924.png

缩略图如上图所示,使用ZoomBox时需要传入一个  ScrollViewer="{Binding ElementName=DesignerScrollViewer}",以便可以通过移动缩略图上的选择框来移动DesignerCanvas

代码文件【ZoomBox.cs】如下:

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
public
class
ZoomBox : Control
{
private
Thumb zoomThumb;
private
Canvas zoomCanvas;
private
Slider zoomSlider;
private
ScaleTransform scaleTransform;
private
DesignerCanvas designerCanvas;
public
ScrollViewer ScrollViewer
{
get
{
return
(ScrollViewer)GetValue(ScrollViewerProperty); }
set
{ SetValue(ScrollViewerProperty, value); }
}
public
static
readonly
DependencyProperty ScrollViewerProperty
=
DependencyProperty.Register(
"
ScrollViewer
"
,
typeof
(ScrollViewer),
typeof
(ZoomBox));
public
override
void
OnApplyTemplate()
{
base
.OnApplyTemplate();
if
(
this
.ScrollViewer
==
null
)
return
;
this
.designerCanvas
=
this
.ScrollViewer.Content
as
DesignerCanvas;
if
(
this
.designerCanvas
==
null
)
throw
new
Exception(
"
DesignerCanvas must not be null!
"
);
this
.zoomThumb
=
Template.FindName(
"
PART_ZoomThumb
"
,
this
)
as
Thumb;
if
(
this
.zoomThumb
==
null
)
throw
new
Exception(
"
PART_ZoomThumb template is missing!
"
);
this
.zoomCanvas
=
Template.FindName(
"
PART_ZoomCanvas
"
,
this
)
as
Canvas;
if
(
this
.zoomCanvas
==
null
)
throw
new
Exception(
"
PART_ZoomCanvas template is missing!
"
);
this
.zoomSlider
=
Template.FindName(
"
PART_ZoomSlider
"
,
this
)
as
Slider;
if
(
this
.zoomSlider
==
null
)
throw
new
Exception(
"
PART_ZoomSlider template is missing!
"
);
this
.designerCanvas.LayoutUpdated
+=
new
EventHandler(
this
.DesignerCanvas_LayoutUpdated);
this
.zoomThumb.DragDelta
+=
new
DragDeltaEventHandler(
this
.Thumb_DragDelta);
this
.zoomSlider.ValueChanged
+=
new
RoutedPropertyChangedEventHandler
<
double
>
(
this
.ZoomSlider_ValueChanged);
this
.scaleTransform
=
new
ScaleTransform();
this
.designerCanvas.LayoutTransform
=
this
.scaleTransform;
}
private
void
ZoomSlider_ValueChanged(
object
sender, RoutedPropertyChangedEventArgs
<
double
>
e)
{
double
scale
=
e.NewValue
/
e.OldValue;
double
halfViewportHeight
=
this
.ScrollViewer.ViewportHeight
/
2
;
double
newVerticalOffset
=
((
this
.ScrollViewer.VerticalOffset
+
halfViewportHeight)
*
scale
-
halfViewportHeight);
double
halfViewportWidth
=
this
.ScrollViewer.ViewportWidth
/
2
;
double
newHorizontalOffset
=
((
this
.ScrollViewer.HorizontalOffset
+
halfViewportWidth)
*
scale
-
halfViewportWidth);
this
.scaleTransform.ScaleX
*=
scale;
this
.scaleTransform.ScaleY
*=
scale;
this
.ScrollViewer.ScrollToHorizontalOffset(newHorizontalOffset);
this
.ScrollViewer.ScrollToVerticalOffset(newVerticalOffset);
}
private
void
Thumb_DragDelta(
object
sender, DragDeltaEventArgs e)
{
double
scale, xOffset, yOffset;
this
.InvalidateScale(
out
scale,
out
xOffset,
out
yOffset);
this
.ScrollViewer.ScrollToHorizontalOffset(
this
.ScrollViewer.HorizontalOffset
+
e.HorizontalChange
/
scale);
this
.ScrollViewer.ScrollToVerticalOffset(
this
.ScrollViewer.VerticalOffset
+
e.VerticalChange
/
scale);
}
private
void
DesignerCanvas_LayoutUpdated(
object
sender, EventArgs e)
{
double
scale, xOffset, yOffset;
this
.InvalidateScale(
out
scale,
out
xOffset,
out
yOffset);
this
.zoomThumb.Width
=
this
.ScrollViewer.ViewportWidth
*
scale;
this
.zoomThumb.Height
=
this
.ScrollViewer.ViewportHeight
*
scale;
Canvas.SetLeft(
this
.zoomThumb, xOffset
+
this
.ScrollViewer.HorizontalOffset
*
scale);
Canvas.SetTop(
this
.zoomThumb, yOffset
+
this
.ScrollViewer.VerticalOffset
*
scale);
}
private
void
InvalidateScale(
out
double
scale,
out
double
xOffset,
out
double
yOffset)
{
//
designer canvas size
double
w
=
this
.designerCanvas.ActualWidth
*
this
.scaleTransform.ScaleX;
double
h
=
this
.designerCanvas.ActualHeight
*
this
.scaleTransform.ScaleY;
//
zoom canvas size
double
x
=
this
.zoomCanvas.ActualWidth;
double
y
=
this
.zoomCanvas.ActualHeight;
double
scaleX
=
x
/
w;
double
scaleY
=
y
/
h;
scale
=
(scaleX
<
scaleY)
?
scaleX : scaleY;
xOffset
=
(x
-
scale
*
w)
/
2
;
yOffset
=
(y
-
scale
*
h)
/
2
;
}

样式文件【ZoomBox.xaml】 如下:

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
<
Setter Property
=
"
Template
"
>
<
Setter.Value
>
<
ControlTemplate TargetType
=
"
{x:Type s:ZoomBox}
"
>
<
Border CornerRadius
=
"
1
"
BorderThickness
=
"
1
"
Background
=
"
#EEE
"
BorderBrush
=
"
DimGray
"
>
<
Expander IsExpanded
=
"
True
"
Background
=
"
Transparent
"
>
<
Border BorderBrush
=
"
DimGray
"
BorderThickness
=
"
0,1,0,0
"
Padding
=
"
0
"
Height
=
"
180
"
>
<
Grid
>
<
Canvas Margin
=
"
5
"
Name
=
"
PART_ZoomCanvas
"
>
<
Canvas.Background
>
<
VisualBrush Stretch
=
"
Uniform
"
Visual
=
"
{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ScrollViewer.Content}
"
/>
</
Canvas.Background
>
<
Thumb Name
=
"
PART_ZoomThumb
"
Cursor
=
"
SizeAll
"
>
<
Thumb.Style
>
<
Style TargetType
=
"
Thumb
"
>
<
Setter Property
=
"
Template
"
>
<
Setter.Value
>
<
ControlTemplate TargetType
=
"
Thumb
"
>
<
Rectangle StrokeThickness
=
"
1
"
Stroke
=
"
Black
"
Fill
=
"
Transparent
"
/>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
</
Thumb.Style
>
</
Thumb
>
</
Canvas
>
</
Grid
>
</
Border
>
<
Expander.Header
>
<
Grid
>
<
Grid.ColumnDefinitions
>
<
ColumnDefinition Width
=
"
Auto
"
/>
<
ColumnDefinition Width
=
"
*
"
/>
</
Grid.ColumnDefinitions
>
<
Slider Name
=
"
PART_ZoomSlider
"
VerticalAlignment
=
"
Center
"
HorizontalAlignment
=
"
Center
"
Margin
=
"
0
"
Ticks
=
"
25,50,75,100,125,150,200,300,400,500
"
Minimum
=
"
25
"
Maximum
=
"
500
"
Value
=
"
100
"
IsSnapToTickEnabled
=
"
True
"
IsMoveToPointEnabled
=
"
False
"
/>
<
TextBlock Text
=
"
{Binding ElementName=PART_ZoomSlider, Path=Value}
"
Grid.Column
=
"
1
"
VerticalAlignment
=
"
Center
"
HorizontalAlignment
=
"
Right
"
Margin
=
"
0,0,14,0
"
/>
<
TextBlock Text
=
"
%
"
Grid.Column
=
"
1
"
VerticalAlignment
=
"
Center
"
HorizontalAlignment
=
"
Right
"
Margin
=
"
1,0,2,0
"
/>
</
Grid
>
</
Expander.Header
>
</
Expander
>
</
Border
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>

框线选择(Rubberband selection)

2010081714310420.png

  • Adorner、Adorner Layer

2010081715425936.png2010081715515290.jpg

框线是通过第一篇说过的Adorner来做的,其实在WPF中很多地方都用到了这个功能,如光标、高亮等。这些Adorner都是放在一个Adorner Layer上,MSDN解释说Adorner Layer是置于一个窗口内所有其它控件之上的。AdornerLayer类只能通过 AdornerLayer.GetAdornerLayer(this) 获取。还可以参考:  

  • DesignerCanvas生成RubberbandAdorner
    当按住鼠标左键点击DesignerCanvas时将生成RubberbandAdorner,代码如下:
    ContractedBlock.gif
    ExpandedBlockStart.gif
    代码
     
    public
    class
    DesignerCanvas : Canvas
    {
    ...
    protected
    override
    void
    OnMouseMove(MouseEventArgs e)
    {
    base
    .OnMouseMove(e);
    if
    (e.LeftButton
    !=
    MouseButtonState.Pressed)
    this
    .dragStartPoint
    =
    null
    ;
    if
    (
    this
    .dragStartPoint.HasValue)
    {
    AdornerLayer adornerLayer
    =
    AdornerLayer.GetAdornerLayer(
    this
    );
    if
    (adornerLayer
    !=
    null
    )
    {
    RubberbandAdorner adorner
    =
    new
    RubberbandAdorner(
    this
    , dragStartPoint);
    if
    (adorner
    !=
    null
    )
    {
    adornerLayer.Add(adorner);
    }
    }
    e.Handled
    =
    true
    ;
    }
    }
    ...
    }
  • 生成RubberbandAdorner : Adorner
    ContractedBlock.gif
    ExpandedBlockStart.gif
    代码
     
    public
    class
    RubberbandAdorner : Adorner
    {
    ....
    private
    Point
    ?
    startPoint, endPoint;
    protected
    override
    void
    OnMouseMove(MouseEventArgs e)
    {
    if
    (e.LeftButton
    ==
    MouseButtonState.Pressed)
    {
    if
    (
    !
    this
    .IsMouseCaptured)
    {
    this
    .CaptureMouse();
    }
    this
    .endPoint
    =
    e.GetPosition(
    this
    );
    this
    .UpdateRubberband();
    this
    .UpdateSelection();
    e.Handled
    =
    true
    ;
    }
    }
    private
    void
    UpdateRubberband()
    {
    double
    left
    =
    Math.Min(
    this
    .startPoint.Value.X,
    this
    .endPoint.Value.X);
    double
    top
    =
    Math.Min(
    this
    .startPoint.Value.Y,
    this
    .endPoint.Value.Y);
    double
    width
    =
    Math.Abs(
    this
    .startPoint.Value.X
    -
    this
    .endPoint.Value.X);
    double
    height
    =
    Math.Abs(
    this
    .startPoint.Value.Y
    -
    this
    .endPoint.Value.Y);
    this
    .rubberband.Width
    =
    width;
    this
    .rubberband.Height
    =
    height;
    Canvas.SetLeft(
    this
    .rubberband, left);
    Canvas.SetTop(
    this
    .rubberband, top);
    }
    private
    void
    UpdateSelection()
    {
    Rect rubberBand
    =
    new
    Rect(
    this
    .startPoint.Value,
    this
    .endPoint.Value);
    foreach
    (DesignerItem item
    in
    this
    .designerCanvas.Children)
    {
    Rect itemRect
    =
    VisualTreeHelper.GetDescendantBounds(item);
    Rect itemBounds
    =
    item.TransformToAncestor
    (designerCanvas).TransformBounds(itemRect);
    if
    (rubberBand.Contains(itemBounds))
    {
    item.IsSelected
    =
    true
    ;
    }
    else
    {
    item.IsSelected
    =
    false
    ;
    }
    }
    }
    ...
    }

     

工具箱Toolbox (drag & drop)

2010081716193277.png

  • Toolbox

工具箱Toolbox是一个ItemsControl控件,它的子是ToolboxItem类型。

代码Toolbox.cs如下:

 

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
public
class
Toolbox : ItemsControl
{
private
Size defaultItemSize
=
new
Size(
65
,
65
);
public
Size DefaultItemSize
{
get
{
return
this
.defaultItemSize; }
set
{
this
.defaultItemSize
=
value; }
}
protected
override
DependencyObject GetContainerForItemOverride()
{
return
new
ToolboxItem();
}
protected
override
bool
IsItemItsOwnContainerOverride(
object
item)
{
return
(item
is
ToolboxItem);
}
}

Toolbox使用WrapPanel显示ToolboxItem,样式文件Toolbox.xaml如下:

 

 

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
<
Style TargetType
=
"
{x:Type s:ToolboxItem}
"
>
<
Setter Property
=
"
Control.Padding
"
Value
=
"
5
"
/>
<
Setter Property
=
"
ContentControl.HorizontalContentAlignment
"
Value
=
"
Stretch
"
/>
<
Setter Property
=
"
ContentControl.VerticalContentAlignment
"
Value
=
"
Stretch
"
/>
<
Setter Property
=
"
ToolTip
"
Value
=
"
{Binding ToolTip}
"
/>
<
Setter Property
=
"
Template
"
>
<
Setter.Value
>
<
ControlTemplate TargetType
=
"
{x:Type s:ToolboxItem}
"
>
<
Grid
>
<
Rectangle Name
=
"
Border
"
StrokeThickness
=
"
1
"
StrokeDashArray
=
"
2
"
Fill
=
"
Transparent
"
SnapsToDevicePixels
=
"
true
"
/>
<
ContentPresenter Content
=
"
{TemplateBinding ContentControl.Content}
"
Margin
=
"
{TemplateBinding Padding}
"
SnapsToDevicePixels
=
"
{TemplateBinding UIElement.SnapsToDevicePixels}
"
/>
</
Grid
>
<
ControlTemplate.Triggers
>
<
Trigger Property
=
"
IsMouseOver
"
Value
=
"
true
"
>
<
Setter TargetName
=
"
Border
"
Property
=
"
Stroke
"
Value
=
"
Gray
"
/>
</
Trigger
>
</
ControlTemplate.Triggers
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
<
Style TargetType
=
"
{x:Type s:Toolbox}
"
>
<
Setter Property
=
"
SnapsToDevicePixels
"
Value
=
"
true
"
/>
<
Setter Property
=
"
Focusable
"
Value
=
"
False
"
/>
<
Setter Property
=
"
Template
"
>
<
Setter.Value
>
<
ControlTemplate
>
<
Border BorderThickness
=
"
{TemplateBinding Border.BorderThickness}
"
Padding
=
"
{TemplateBinding Control.Padding}
"
BorderBrush
=
"
{TemplateBinding Border.BorderBrush}
"
Background
=
"
{TemplateBinding Panel.Background}
"
SnapsToDevicePixels
=
"
True
"
>
<
ScrollViewer VerticalScrollBarVisibility
=
"
Auto
"
>
<
ItemsPresenter SnapsToDevicePixels
=
"
{TemplateBinding UIElement.SnapsToDevicePixels}
"
/>
</
ScrollViewer
>
</
Border
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
<
Setter Property
=
"
ItemsPanel
"
>
<
Setter.Value
>
<
ItemsPanelTemplate
>
<
WrapPanel Margin
=
"
0,5,0,5
"
ItemHeight
=
"
{Binding Path=DefaultItemSize.Height, RelativeSource={RelativeSource AncestorType=s:Toolbox}}
"
ItemWidth
=
"
{Binding Path=DefaultItemSize.Width, RelativeSource={RelativeSource AncestorType=s:Toolbox}}
"
/>
</
ItemsPanelTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>

 

  • ToolboxItem

ToolboxItem是显示在工具箱中的对象,我们可以通过鼠标点击它进行选择,然后拖拽到DesignerCanvas来生成一个设计对象,示例中是通过XamlWriter.Save保存到DataObject,然后在DesignerCanvas接收这个对象,这部分在进行自己的设计器开发时会进行更改

ToolboxItem的代码如下:

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
public
class
ToolboxItem : ContentControl
{
private
Point
?
dragStartPoint
=
null
;
static
ToolboxItem()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof
(ToolboxItem),
new
FrameworkPropertyMetadata(
typeof
(ToolboxItem)));
}
protected
override
void
OnPreviewMouseDown(MouseButtonEventArgs e)
{
base
.OnPreviewMouseDown(e);
this
.dragStartPoint
=
new
Point
?
(e.GetPosition(
this
));
}
protected
override
void
OnMouseMove(MouseEventArgs e)
{
base
.OnMouseMove(e);
if
(e.LeftButton
!=
MouseButtonState.Pressed)
{
this
.dragStartPoint
=
null
;
}
if
(
this
.dragStartPoint.HasValue)
{
Point position
=
e.GetPosition(
this
);
if
((SystemParameters.MinimumHorizontalDragDistance
<=
Math.Abs((
double
)(position.X
-
this
.dragStartPoint.Value.X)))
||
(SystemParameters.MinimumVerticalDragDistance
<=
Math.Abs((
double
)(position.Y
-
this
.dragStartPoint.Value.Y))))
{
string
xamlString
=
XamlWriter.Save(
this
.Content);
DataObject dataObject
=
new
DataObject(
"
DESIGNER_ITEM
"
, xamlString);
if
(dataObject
!=
null
)
{
DragDrop.DoDragDrop(
this
, dataObject, DragDropEffects.Copy);
}
}
e.Handled
=
true
;
}
}
}

DesignerItem增加IsSelected属性

DesignerItem增加是否选择属性,代码如下:

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
public
class
DesignerItem : ContentControl
{
public
bool
IsSelected
{
get
{
return
(
bool
)GetValue(IsSelectedProperty); }
set
{ SetValue(IsSelectedProperty, value); }
}
public
static
readonly
DependencyProperty IsSelectedProperty
=
DependencyProperty.Register(
"
IsSelected
"
,
typeof
(
bool
),
typeof
(DesignerItem),
new
FrameworkPropertyMetadata(
false
));
...
}

在MouseDown事件时会去设置IsSelected属性:

 

 

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
protected
override
void
OnPreviewMouseDown(MouseButtonEventArgs e)
{
base
.OnPreviewMouseDown(e);
DesignerCanvas designer
=
VisualTreeHelper.GetParent(
this
)
as
DesignerCanvas;
if
(designer
!=
null
)
{
if
((Keyboard.Modifiers
&
(ModifierKeys.Shift
|
ModifierKeys.Control))
!=
ModifierKeys.None)
{
this
.IsSelected
=
!
this
.IsSelected;
}
else
{
if
(
!
this
.IsSelected)
{
designer.DeselectAll();
this
.IsSelected
=
true
;
}
}
}
e.Handled
=
false
;
}

IsSelected属性触发ResizeDecorator是否显示:

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
<
Style TargetType
=
"
{x:Type s:DesignerItem}
"
>
<
Setter Property
=
"
MinHeight
"
Value
=
"
50
"
/>
<
Setter Property
=
"
MinWidth
"
Value
=
"
50
"
/>
<
Setter Property
=
"
SnapsToDevicePixels
"
Value
=
"
true
"
/>
<
Setter Property
=
"
Template
"
>
<
Setter.Value
>
<
ControlTemplate TargetType
=
"
{x:Type s:DesignerItem}
"
>
<
Grid DataContext
=
"
{Binding RelativeSource={RelativeSource TemplatedParent},
Path
=
.}
"
>
<
s:MoveThumb
x:Name
=
"
PART_MoveThumb
"
Cursor
=
"
SizeAll
"
Template
=
"
{StaticResource MoveThumbTemplate}
"
/>
<
ContentPresenter
x:Name
=
"
PART_ContentPresenter
"
Content
=
"
{TemplateBinding ContentControl.Content}
"
Margin
=
"
{TemplateBinding Padding}
"
/>
<
s:ResizeDecorator x:Name
=
"
PART_DesignerItemDecorator
"
/>
</
Grid
>
<
ControlTemplate.Triggers
>
<
Trigger Property
=
"
IsSelected
"
Value
=
"
True
"
>
<
Setter TargetName
=
"
PART_DesignerItemDecorator
"
Property
=
"
ShowDecorator
"
Value
=
"
True
"
/>
</
Trigger
>
</
ControlTemplate.Triggers
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>

DesignerItem支持移动选择区域

2010081716455283.png   2010081716461420.png

DesignerItem默认允许移动的是一个透明的矩形区域,如上图左边这个。我们一般希望点击这个形状内部才允许移动和选择,这时候我们可以通过DesignerItem.MoveThumbTemplate来更改这个支持Move的区域,代码如下:

 
<
Path Stroke
=
"
Red
"
StrokeThickness
=
"
5
"
Stretch
=
"
Fill
"
IsHitTestVisible
=
"
false
"
Data
=
"
M 9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7 Z
"
>
<
s:DesignerItem.
MoveThumbTemplate
>
<
ControlTemplate
>
<
Path Data
=
"
M 9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7 Z
"
Fill
=
"
Transparent
"
Stretch
=
"
Fill
"
/>
</
ControlTemplate
>
</
s:DesignerItem.
MoveThumbTemplate
 
>
</
Path
>

 

 

 

欢迎转载,转载请注明:转载自 [ ]

转载于:https://www.cnblogs.com/zhoujg/archive/2010/08/17/1801427.html

转载地址:https://blog.csdn.net/weixin_30687587/article/details/94987143 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:团队编程-项目作业6-程序维护
下一篇:My97DatePicker日期控件用法

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年03月13日 15时55分53秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

编写python程序、计算账户余额_小明有20w存款存在余额宝中,按余额宝年收益为3.35%计算,用Python编写程序计算,多少年后小明的存款达到30w?... 2019-04-21
python 公众号引流_公众号引流方法有哪些? 2019-04-21
java 减少内存_java中减少内存占用小技巧 2019-04-21
centos 7 mysql图形界面_centos7-vnstat图形界面搭建 2019-04-21
java 防渗透_「java、工程师工作经验怎么写」-看准网 2019-04-21
java中跳出当前循环怎么做_在java中,如何跳出当前的多重循环? 2019-04-21
java程序中执行maven_java – 将一个enviornment变量传递给Maven中的已执行进程 2019-04-21
java16下载_java lombok下载 2019-04-21
python 图像处理与识别书籍_Python图像处理之识别图像中的文字(实例讲解) 2019-04-21
java安全初始化_java安全编码指南之:声明和初始化 2019-04-21
java jstat gc_分析JVM GC及内存情况的方法 2019-04-21
php pclzip.lib.php,php使用pclzip类实现文件压缩的方法(附pclzip类下载地址) 2019-04-21
php dns更新,php_mzdns: 站群,大量域名 通过 dns 服务商 api 批量添加 ip 工具。你懂的~ 基于 mzphp2 框架。... 2019-04-21
jdk 1.8 java.policy,JDK1.8 导致系统报错:java.security.InvalidKeyException:illegal Key Size 2019-04-21
php linux权限,Linux权限详细介绍 2019-04-21
典型环节的matlab仿真分析,典型环节的MATLAB仿真.doc 2019-04-21
Php contenttype类型,各种类型文件的Content Type 2019-04-21
php使用redis持久化,redis如何持久化 2019-04-21
php7.1解压包安装,【Swoole】php7.1安装swoole扩展 2019-04-21
linux centos删除安装的包,CentOS yum认为已删除的软件包仍在安装中 2019-04-21