This commit is contained in:
GG Z
2025-07-31 20:12:01 +08:00
parent 6d96da6f90
commit 4f6cd2137c
292 changed files with 8276 additions and 10827 deletions

View File

@@ -0,0 +1,246 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media;
namespace AntDesign.WPF.Controls
{
/// <summary>
/// Alert component for feedback.
/// </summary>
[ContentProperty("Message")]
[TemplatePart(Name = PART_Close, Type = typeof(ButtonBase))]
public class Alert : Control
{
#region Fields
private const string PART_Close = "PART_Close";
private ButtonBase close;
#endregion
#region Events
public static readonly RoutedEvent ClosingEvent =
EventManager.RegisterRoutedEvent("Closing", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Alert));
/// <summary>
/// Occurs when closing the tag.
/// </summary>
public event RoutedEventHandler Closing
{
add { AddHandler(ClosingEvent, value); }
remove { RemoveHandler(ClosingEvent, value); }
}
public static readonly RoutedEvent ClosedEvent =
EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Alert));
/// <summary>
/// Occurs when a Tag is closed and is no longer visible.
/// </summary>
public event RoutedEventHandler Closed
{
add { AddHandler(ClosedEvent, value); }
remove { RemoveHandler(ClosedEvent, value); }
}
#endregion
#region Properties
public static readonly DependencyProperty BannerProperty =
DependencyProperty.Register("Banner", typeof(bool), typeof(Alert), new PropertyMetadata(false));
/// <summary>
/// Gets/sets whether to show as banner.
/// </summary>
public bool Banner
{
get { return (bool)GetValue(BannerProperty); }
set { SetValue(BannerProperty, value); }
}
public static readonly DependencyProperty ClosableProperty =
DependencyProperty.Register("Closable", typeof(bool?), typeof(Alert), new PropertyMetadata(null, OnClosableChanged));
/// <summary>
/// Gets/sets whether alert can be closed.
/// </summary>
public bool? Closable
{
get { return (bool?)GetValue(ClosableProperty); }
set { SetValue(ClosableProperty, value); }
}
private static void OnClosableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Alert).SetCloseButtonVisibility();
}
private void SetCloseButtonVisibility()
{
if (close != null)
{
var visible = Closable.HasValue ? Closable.Value : CloseText != null;
close.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
}
}
public static readonly DependencyProperty CloseTextProperty =
DependencyProperty.Register("CloseText", typeof(object), typeof(Alert), new PropertyMetadata(null, OnCloseTextChanged));
/// <summary>
/// Gets/sets close text to show.
/// </summary>
public object CloseText
{
get { return GetValue(CloseTextProperty); }
set { SetValue(CloseTextProperty, value); }
}
private static void OnCloseTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Alert).SetCloseButton();
}
private void SetCloseButton()
{
if (close != null)
{
close.Content = CloseText != null ? CloseText : new Icon() { Type = "close" };
}
SetCloseButtonVisibility();
}
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(object), typeof(Alert), new PropertyMetadata(null));
/// <summary>
/// Gets/sets additional content of alert.
/// </summary>
public object Description
{
get { return GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(string), typeof(Alert), new PropertyMetadata(string.Empty));
/// <summary>
/// Gets/sets the icon type of the alert.
/// </summary>
public string Icon
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(object), typeof(Alert), new PropertyMetadata(null));
/// <summary>
/// Gets/sets content of alert.
/// </summary>
public object Message
{
get { return GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty ShowIconProperty =
DependencyProperty.Register("ShowIcon", typeof(bool), typeof(Alert), new PropertyMetadata(false));
/// <summary>
/// Gets/sets whether to show icon.
/// </summary>
public bool ShowIcon
{
get { return (bool)GetValue(ShowIconProperty); }
set { SetValue(ShowIconProperty, value); }
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register("Type", typeof(AlertType), typeof(Alert), new PropertyMetadata(AlertType.Info));
/// <summary>
/// Gets/sets the type of alert.
/// </summary>
public AlertType Type
{
get { return (AlertType)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty IconBrushProperty =
DependencyProperty.Register("IconBrush", typeof(Brush), typeof(Alert), new PropertyMetadata(null));
/// <summary>
/// Gets/sets the alert icon brush.
/// </summary>
public Brush IconBrush
{
get { return (Brush)GetValue(IconBrushProperty); }
set { SetValue(IconBrushProperty, value); }
}
#endregion
#region Constructors
static Alert()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Alert), new FrameworkPropertyMetadata(typeof(Alert)));
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
close = GetTemplateChild(PART_Close) as ButtonBase;
if (close != null)
{
Loaded -= OnLoaded;
Loaded += OnLoaded;
close.Click -= OnRaiseClosingEvent;
close.Click += OnRaiseClosingEvent;
}
SetCloseButton();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Closing -= OnClosing;
Closing += OnClosing;
}
private void OnRaiseClosingEvent(object sender, RoutedEventArgs e)
{
e.Handled = true;
RaiseEvent(new RoutedEventArgs(ClosingEvent, this));
}
private void OnClosing(object sender, RoutedEventArgs e)
{
SetCurrentValue(VisibilityProperty, Visibility.Collapsed);
RaiseEvent(new RoutedEventArgs(ClosedEvent, this));
}
#endregion
}
public enum AlertType : byte
{
Success, Info, Warning, Error
}
}

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System;
using System.Windows;
using System.Windows.Markup;
using System.ComponentModel;
using System.Windows.Media;
namespace AntDesign.WPF.Controls
{
/// <summary>
/// 图标控件
/// </summary>
public class AntIcon : System.Windows.Controls.Control
{
private static readonly Lazy<IDictionary<AntIconKey, string>> _dataList
= new Lazy<IDictionary<AntIconKey, string>>(AntIconDataFactory.Create);
static AntIcon()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AntIcon), new FrameworkPropertyMetadata(typeof(AntIcon)));
}
public static readonly DependencyProperty KeyProperty
= DependencyProperty.Register(nameof(Key), typeof(AntIconKey), typeof(AntIcon), new PropertyMetadata(default(AntIconKey), KeyPropertyChangedCallback));
private static void KeyPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
=> ((AntIcon)dependencyObject).UpdateData();
private static readonly DependencyPropertyKey DataPropertyKey
= DependencyProperty.RegisterReadOnly(nameof(Data), typeof(string), typeof(AntIcon), new PropertyMetadata(""));
// ReSharper disable once StaticMemberInGenericType
public static readonly DependencyProperty DataProperty = DataPropertyKey.DependencyProperty;
/// <summary>
/// Gets or sets the icon to display.
/// </summary>
public AntIconKey Key
{
get => (AntIconKey)GetValue(KeyProperty);
set => SetValue(KeyProperty, value);
}
/// <summary>
/// Gets the icon path data for the current <see cref="Key"/>.
/// </summary>
[TypeConverter(typeof(GeometryConverter))]
public string? Data
{
get => (string?)GetValue(DataProperty);
private set => SetValue(DataPropertyKey, value);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
UpdateData();
}
private void UpdateData()
{
string? data = null;
_dataList.Value?.TryGetValue(Key, out data);
Data = data;
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,456 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntDesign.WPF.Controls
{
public enum AntIconKey
{
//StepBackward,
//StepForward,
//FastForward,
//FastBackward,
//Shrink,
//ArrowsAlt,
//Left,
//Up,
//Down,
//Right,
//CaretRight,
//CaretUp,
//CaretDown,
//CaretLeft,
//DownCircle,
//RightCircle,
//LeftCircle,
//UpCircle,
//DoubleLeft,
//DoubleRight,
//VerticalRight,
//VerticalLeft,
//VerticalAlignTop,
//VerticalAlignMiddle,
//VerticalAlignBottom,
AccountBookOutlined,
AimOutlined,
AlertOutlined,
AlibabaOutlined,
AlignCenterOutlined,
AlignLeftOutlined,
AlignRightOutlined,
AlipayCircleOutlined,
AlipayOutlined,
AliwangwangOutlined,
AliyunOutlined,
AmazonOutlined,
AndroidOutlined,
AntCloudOutlined,
AntDesignOutlined,
ApartmentOutlined,
ApiOutlined,
AppleOutlined,
AppstoreAddOutlined,
AppstoreOutlined,
AreaChartOutlined,
ArrowDownOutlined,
ArrowLeftOutlined,
ArrowRightOutlined,
ArrowsAltOutlined,
ArrowUpOutlined,
AudioMutedOutlined,
AudioOutlined,
AuditOutlined,
BackwardOutlined,
BankOutlined,
BarChartOutlined,
BarcodeOutlined,
BarsOutlined,
BehanceOutlined,
BehanceSquareOutlined,
BellOutlined,
BgColorsOutlined,
BlockOutlined,
BoldOutlined,
BookOutlined,
BorderBottomOutlined,
BorderHorizontalOutlined,
BorderInnerOutlined,
BorderLeftOutlined,
BorderlessTableOutlined,
BorderOuterOutlined,
BorderOutlined,
BorderRightOutlined,
BorderTopOutlined,
BorderVerticleOutlined,
BoxPlotOutlined,
BranchesOutlined,
BugOutlined,
BuildOutlined,
BulbOutlined,
CalculatorOutlined,
CalendarOutlined,
CameraOutlined,
CaretDownOutlined,
CaretLeftOutlined,
CaretRightOutlined,
CaretUpOutlined,
CarOutlined,
CarryOutOutlined,
CheckCircleOutlined,
CheckOutlined,
CheckSquareOutlined,
ChromeOutlined,
CiCircleOutlined,
CiOutlined,
ClearOutlined,
ClockCircleOutlined,
CloseCircleOutlined,
CloseOutlined,
CloseSquareOutlined,
CloudDownloadOutlined,
CloudOutlined,
CloudServerOutlined,
CloudSyncOutlined,
CloudUploadOutlined,
ClusterOutlined,
CodeOutlined,
CodepenCircleOutlined,
CodepenOutlined,
CodeSandboxOutlined,
CoffeeOutlined,
ColumnHeightOutlined,
ColumnWidthOutlined,
CommentOutlined,
CompassOutlined,
CompressOutlined,
ConsoleSqlOutlined,
ContactsOutlined,
ContainerOutlined,
ControlOutlined,
CopyOutlined,
CopyrightCircleOutlined,
CopyrightOutlined,
CreditCardOutlined,
CrownOutlined,
CustomerServiceOutlined,
DashboardOutlined,
DashOutlined,
DatabaseOutlined,
DeleteColumnOutlined,
DeleteOutlined,
DeleteRowOutlined,
DeliveredProcedureOutlined,
DeploymentUnitOutlined,
DesktopOutlined,
DiffOutlined,
DingdingOutlined,
DingtalkOutlined,
DisconnectOutlined,
DislikeOutlined,
DollarCircleOutlined,
DollarOutlined,
DotChartOutlined,
DoubleLeftOutlined,
DoubleRightOutlined,
DownCircleOutlined,
DownloadOutlined,
DownOutlined,
DownSquareOutlined,
DragOutlined,
DribbbleOutlined,
DribbbleSquareOutlined,
DropboxOutlined,
EditOutlined,
EllipsisOutlined,
EnterOutlined,
EnvironmentOutlined,
EuroCircleOutlined,
EuroOutlined,
ExceptionOutlined,
ExclamationCircleOutlined,
ExclamationOutlined,
ExpandAltOutlined,
ExpandOutlined,
ExperimentOutlined,
ExportOutlined,
EyeInvisibleOutlined,
EyeOutlined,
FacebookOutlined,
FallOutlined,
FastBackwardOutlined,
FastForwardOutlined,
FieldBinaryOutlined,
FieldNumberOutlined,
FieldStringOutlined,
FieldTimeOutlined,
FileAddOutlined,
FileDoneOutlined,
FileExcelOutlined,
FileExclamationOutlined,
FileGifOutlined,
FileImageOutlined,
FileJpgOutlined,
FileMarkdownOutlined,
FileOutlined,
FilePdfOutlined,
FilePptOutlined,
FileProtectOutlined,
FileSearchOutlined,
FileSyncOutlined,
FileTextOutlined,
FileUnknownOutlined,
FileWordOutlined,
FileZipOutlined,
FilterOutlined,
FireOutlined,
FlagOutlined,
FolderAddOutlined,
FolderOpenOutlined,
FolderOutlined,
FolderViewOutlined,
FontColorsOutlined,
FontSizeOutlined,
ForkOutlined,
FormatPainterOutlined,
FormOutlined,
ForwardOutlined,
FrownOutlined,
FullscreenExitOutlined,
FullscreenOutlined,
FunctionOutlined,
FundOutlined,
FundProjectionScreenOutlined,
FundViewOutlined,
FunnelPlotOutlined,
GatewayOutlined,
GifOutlined,
GiftOutlined,
GithubOutlined,
GitlabOutlined,
GlobalOutlined,
GoldOutlined,
GoogleOutlined,
GooglePlusOutlined,
GroupOutlined,
HddOutlined,
HeartOutlined,
HeatMapOutlined,
HighlightOutlined,
HistoryOutlined,
HomeOutlined,
HourglassOutlined,
Html5Outlined,
IdcardOutlined,
IeOutlined,
ImportOutlined,
InboxOutlined,
InfoCircleOutlined,
InfoOutlined,
InsertRowAboveOutlined,
InsertRowBelowOutlined,
InsertRowLeftOutlined,
InsertRowRightOutlined,
InstagramOutlined,
InsuranceOutlined,
InteractionOutlined,
IssuesCloseOutlined,
ItalicOutlined,
KeyOutlined,
LaptopOutlined,
LayoutOutlined,
LeftCircleOutlined,
LeftOutlined,
LeftSquareOutlined,
LikeOutlined,
LineChartOutlined,
LineHeightOutlined,
LineOutlined,
LinkedinOutlined,
LinkOutlined,
Loading3QuartersOutlined,
LoadingOutlined,
LockOutlined,
LoginOutlined,
LogoutOutlined,
MacCommandOutlined,
MailOutlined,
ManOutlined,
MedicineBoxOutlined,
MediumOutlined,
MediumWorkmarkOutlined,
MehOutlined,
MenuFoldOutlined,
MenuOutlined,
MenuUnfoldOutlined,
MergeCellsOutlined,
MessageOutlined,
MinusCircleOutlined,
MinusOutlined,
MinusSquareOutlined,
MobileOutlined,
MoneyCollectOutlined,
MonitorOutlined,
MoreOutlined,
NodeCollapseOutlined,
NodeExpandOutlined,
NodeIndexOutlined,
NotificationOutlined,
NumberOutlined,
OneToOneOutlined,
OrderedListOutlined,
PaperClipOutlined,
PartitionOutlined,
PauseCircleOutlined,
PauseOutlined,
PayCircleOutlined,
PercentageOutlined,
PhoneOutlined,
PicCenterOutlined,
PicLeftOutlined,
PicRightOutlined,
PictureOutlined,
PieChartOutlined,
PlayCircleOutlined,
PlaySquareOutlined,
PlusCircleOutlined,
PlusOutlined,
PlusSquareOutlined,
PoundCircleOutlined,
PoundOutlined,
PoweroffOutlined,
PrinterOutlined,
ProfileOutlined,
ProjectOutlined,
PropertySafetyOutlined,
PullRequestOutlined,
PushpinOutlined,
QqOutlined,
QrcodeOutlined,
QuestionCircleOutlined,
QuestionOutlined,
RadarChartOutlined,
RadiusBottomleftOutlined,
RadiusBottomrightOutlined,
RadiusSettingOutlined,
RadiusUpleftOutlined,
RadiusUprightOutlined,
ReadOutlined,
ReconciliationOutlined,
RedditOutlined,
RedEnvelopeOutlined,
RedoOutlined,
ReloadOutlined,
RestOutlined,
RetweetOutlined,
RightCircleOutlined,
RightOutlined,
RightSquareOutlined,
RiseOutlined,
RobotOutlined,
RocketOutlined,
RollbackOutlined,
RotateLeftOutlined,
RotateRightOutlined,
SafetyCertificateOutlined,
SafetyOutlined,
SaveOutlined,
ScanOutlined,
ScheduleOutlined,
ScissorOutlined,
SearchOutlined,
SecurityScanOutlined,
SelectOutlined,
SendOutlined,
SettingOutlined,
ShakeOutlined,
ShareAltOutlined,
ShopOutlined,
ShoppingCartOutlined,
ShoppingOutlined,
ShrinkOutlined,
SisternodeOutlined,
SketchOutlined,
SkinOutlined,
SkypeOutlined,
SlackOutlined,
SlackSquareOutlined,
SlidersOutlined,
SmallDashOutlined,
SmileOutlined,
SnippetsOutlined,
SolutionOutlined,
SortAscendingOutlined,
SortDescendingOutlined,
SoundOutlined,
SplitCellsOutlined,
StarOutlined,
StockOutlined,
StopOutlined,
StrikethroughOutlined,
SubnodeOutlined,
SwapLeftOutlined,
SwapOutlined,
SwapRightOutlined,
SwitcherOutlined,
SyncOutlined,
TableOutlined,
TabletOutlined,
TagOutlined,
TagsOutlined,
TaobaoCircleOutlined,
TaobaoOutlined,
TeamOutlined,
ThunderboltOutlined,
ToolOutlined,
ToTopOutlined,
TrademarkCircleOutlined,
TrademarkOutlined,
TransactionOutlined,
TranslationOutlined,
TrophyOutlined,
TwitterOutlined,
UnderlineOutlined,
UndoOutlined,
UngroupOutlined,
UnlockOutlined,
UnorderedListOutlined,
UpCircleOutlined,
UploadOutlined,
UpOutlined,
UpSquareOutlined,
UsbOutlined,
UserAddOutlined,
UserDeleteOutlined,
UsergroupAddOutlined,
UsergroupDeleteOutlined,
UserOutlined,
UserSwitchOutlined,
Vector,
VerifiedOutlined,
VerticalAlignMiddleOutlined,
VerticalAlignTopOutlined,
VerticalLeftOutlined,
VerticalRightOutlined,
VideoCameraAddOutlined,
VideoCameraOutlined,
WalletOutlined,
WarningOutlined,
WechatOutlined,
WeiboCircleOutlined,
WeiboOutlined,
WeiboSquareOutlined,
WhatsAppOutlined,
WifiOutlined,
WindowsOutlined,
WomanOutlined,
YahooOutlined,
YoutubeOutlined,
YuqueOutlined,
ZhihuOutlined,
ZoomInOutlined,
ZoomOutOutlined,
}
}

View File

@@ -0,0 +1,748 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using AntDesign.WPF.Utils;
namespace AntDesign.WPF.Controls;
/// <summary>
/// Draws a border, background, or both around another element.
/// </summary>
public class AntdBorder : Decorator
{
#region Fields
private StreamGeometry backgroundGeometryCache;
private StreamGeometry upperLeftCache;
private StreamGeometry upperRightCache;
private StreamGeometry lowerRightCache;
private StreamGeometry lowerLeftCache;
private bool useComplexRender;
private Pen leftPenCache;
private Pen topPenCache;
private Pen rightPenCache;
private Pen bottomPenCache;
#endregion
#region Properties
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
"Background",
typeof(Brush),
typeof(AntdBorder),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender
));
/// <summary>
/// Gets or sets the brush that fills the area between the bounds of a Border.
/// </summary>
public Brush Background
{
get { return (Brush)GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register(
"BorderBrush",
typeof(Brush),
typeof(AntdBorder),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
OnClearPenCache
));
/// <summary>
/// Gets or sets the brush that draws the outer border color.
/// </summary>
public Brush BorderBrush
{
get { return (Brush)GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); }
}
public static readonly DependencyProperty BorderThicknessProperty = DependencyProperty.Register(
"BorderThickness",
typeof(Thickness),
typeof(AntdBorder),
new FrameworkPropertyMetadata(
new Thickness(),
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
OnClearPenCache
),
IsThicknessValid);
/// <summary>
/// Gets or sets the relative thickness of a border.
/// </summary>
public Thickness BorderThickness
{
get { return (Thickness)GetValue(BorderThicknessProperty); }
set { SetValue(BorderThicknessProperty, value); }
}
private static void OnClearPenCache(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var border = (AntdBorder)d;
border.leftPenCache = border.topPenCache = border.rightPenCache = border.bottomPenCache = null;
}
private static bool IsThicknessValid(object value)
{
return ThicknessUtil.IsValid((Thickness)value, false, false, false, false);
}
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
"CornerRadius",
typeof(CornerRadius),
typeof(AntdBorder),
new FrameworkPropertyMetadata(
new CornerRadius(),
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender
),
IsCornerRadiusValid);
/// <summary>
/// Gets or sets a value that represents the degree to which the corners of a Border are rounded.
/// </summary>
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
private static bool IsCornerRadiusValid(object value)
{
return CornerRadiusUtil.IsValid((CornerRadius)value, false, false, false, false);
}
public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register(
"Padding",
typeof(Thickness),
typeof(AntdBorder),
new FrameworkPropertyMetadata(
new Thickness(),
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender
),
IsThicknessValid);
/// <summary>
/// Gets or sets a thickness value that describes the amount of space between a border and its child element.
/// </summary>
public Thickness Padding
{
get { return (Thickness)GetValue(PaddingProperty); }
set { SetValue(PaddingProperty, value); }
}
public static readonly DependencyProperty BorderStyleProperty = DependencyProperty.Register(
"BorderStyle",
typeof(BorderStyle),
typeof(AntdBorder),
new FrameworkPropertyMetadata(
BorderStyle.Solid,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
OnClearPenCache
));
/// <summary>
/// Gets or sets the border style.
/// </summary>
public BorderStyle BorderStyle
{
get { return (BorderStyle)GetValue(BorderStyleProperty); }
set { SetValue(BorderStyleProperty, value); }
}
#endregion
#region Overrides
/// <summary>
/// Updates DesiredSize of the Border. Called by parent UIElement. This is the first pass of layout.
/// </summary>
/// <remarks>
/// Border determines its desired size it needs from the specified border the child: its sizing
/// properties, margin, and requested size.
/// </remarks>
/// <param name="constraint">Constraint size is an "upper limit" that the return value should not exceed.</param>
/// <returns>The Decorator's desired size.</returns>
protected override Size MeasureOverride(Size constraint)
{
var child = Child;
var desiredSize = new Size();
var borders = BorderThickness;
if (UseLayoutRounding)
{
var dpi = DpiUtil.GetDpi(this);
borders = new Thickness(UIElementUtil.RoundLayoutValue(borders.Left, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Top, dpi.DpiScaleY),
UIElementUtil.RoundLayoutValue(borders.Right, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Bottom, dpi.DpiScaleY));
}
// Compute the total size required
var borderSize = ThicknessUtil.CollapseThickness(borders);
var paddingSize = ThicknessUtil.CollapseThickness(Padding);
// If we have a child
if (child != null)
{
// Combine into total decorating size
var combined = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
// Remove size of border only from child's reference size.
var childConstraint = new Size(Math.Max(0.0, constraint.Width - combined.Width),
Math.Max(0.0, constraint.Height - combined.Height));
child.Measure(childConstraint);
var childSize = child.DesiredSize;
// Now use the returned size to drive our size, by adding back the margins, etc.
desiredSize.Width = childSize.Width + combined.Width;
desiredSize.Height = childSize.Height + combined.Height;
}
else
{
// Combine into total decorating size
desiredSize = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
}
return desiredSize;
}
/// <summary>
/// Border computes the position of its single child and applies its child's alignments to the child.
/// </summary>
/// <param name="finalSize">The size reserved for this element by the parent</param>
/// <returns>The actual ink area of the element, typically the same as finalSize</returns>
protected override Size ArrangeOverride(Size finalSize)
{
var borders = BorderThickness;
if (UseLayoutRounding)
{
var dpi = DpiUtil.GetDpi(this);
borders = new Thickness(UIElementUtil.RoundLayoutValue(borders.Left, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Top, dpi.DpiScaleY),
UIElementUtil.RoundLayoutValue(borders.Right, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Bottom, dpi.DpiScaleY));
}
var boundRect = new Rect(finalSize);
var innerRect = RectUtil.Deflate(boundRect, borders);
// arrange child
var child = Child;
if (child != null)
{
Rect childRect = RectUtil.Deflate(innerRect, Padding);
child.Arrange(childRect);
}
var radius = CornerRadius;
useComplexRender = !CornerRadiusUtil.IsUniform(radius) || !ThicknessUtil.IsUniform(borders);
backgroundGeometryCache = upperLeftCache = upperRightCache = lowerRightCache = lowerLeftCache = null;
if (useComplexRender)
{
// calculate border / background rendering geometry
if (!DoubleUtil.IsZero(boundRect.Width) && !DoubleUtil.IsZero(boundRect.Height))
{
var outerRadii = new Radii(boundRect, radius, borders, true);
// Upper-right corner
var radiusX = boundRect.TopRight.X - outerRadii.TopRight.X;
var radiusY = outerRadii.RightTop.Y - boundRect.TopRight.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
upperRightCache = GenerateRoundedGeometry(outerRadii.TopRight, outerRadii.RightTop, new Size(radiusX, radiusY));
}
// Lower-right corner
radiusX = boundRect.BottomRight.X - outerRadii.BottomRight.X;
radiusY = boundRect.BottomRight.Y - outerRadii.RightBottom.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
lowerRightCache = GenerateRoundedGeometry(outerRadii.RightBottom, outerRadii.BottomRight, new Size(radiusX, radiusY));
}
// Lower-left corner
radiusX = outerRadii.BottomLeft.X - boundRect.BottomLeft.X;
radiusY = boundRect.BottomLeft.Y - outerRadii.LeftBottom.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
lowerLeftCache = GenerateRoundedGeometry(outerRadii.BottomLeft, outerRadii.LeftBottom, new Size(radiusX, radiusY));
}
// Upper-left corner
radiusX = outerRadii.TopLeft.X - boundRect.TopLeft.X;
radiusY = outerRadii.LeftTop.Y - boundRect.TopLeft.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
upperLeftCache = GenerateRoundedGeometry(outerRadii.LeftTop, outerRadii.TopLeft, new Size(radiusX, radiusY));
}
}
if (!DoubleUtil.IsZero(innerRect.Width) && !DoubleUtil.IsZero(innerRect.Height))
{
var innerRadii = new Radii(innerRect, radius, borders, false);
var backgroundGeometry = new StreamGeometry();
using (StreamGeometryContext sc = backgroundGeometry.Open())
{
// create the border geometry
sc.BeginFigure(innerRadii.TopLeft, true /* is filled */, true /* is closed */);
// Top line
sc.LineTo(innerRadii.TopRight, true /* is stroked */, false /* is smooth join */);
// Upper-right corners
var radiusX = innerRect.TopRight.X - innerRadii.TopRight.X;
var radiusY = innerRadii.RightTop.Y - innerRect.TopRight.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
sc.ArcTo(innerRadii.RightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Right line
sc.LineTo(innerRadii.RightBottom, true /* is stroked */, false /* is smooth join */);
// Lower-right corners
radiusX = innerRect.BottomRight.X - innerRadii.BottomRight.X;
radiusY = innerRect.BottomRight.Y - innerRadii.RightBottom.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
sc.ArcTo(innerRadii.BottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Bottom line
sc.LineTo(innerRadii.BottomLeft, true /* is stroked */, false /* is smooth join */);
// Lower-left corners
radiusX = innerRadii.BottomLeft.X - innerRect.BottomLeft.X;
radiusY = innerRect.BottomLeft.Y - innerRadii.LeftBottom.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
sc.ArcTo(innerRadii.LeftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Left line
sc.LineTo(innerRadii.LeftTop, true /* is stroked */, false /* is smooth join */);
// Upper-left corners
radiusX = innerRadii.TopLeft.X - innerRect.TopLeft.X;
radiusY = innerRadii.LeftTop.Y - innerRect.TopLeft.Y;
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
{
sc.ArcTo(innerRadii.TopLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
}
backgroundGeometry.Freeze();
backgroundGeometryCache = backgroundGeometry;
}
}
return finalSize;
}
protected override void OnRender(DrawingContext dc)
{
if (useComplexRender)
{
ComplexRender(dc);
} else
{
SimpleRender(dc);
}
}
#endregion
#region Private Methods
private void SimpleRender(DrawingContext dc)
{
var useLayoutRounding = UseLayoutRounding;
var dpi = DpiUtil.GetDpi(this);
Brush brush;
var borderStyle = BorderStyle;
var borders = BorderThickness;
var cornerRadius = CornerRadius;
var outerCornerRadius = cornerRadius.TopLeft; // Already validated that all corners have the same radius
var roundedCorners = !DoubleUtil.IsZero(outerCornerRadius);
var width = RenderSize.Width;
var height = RenderSize.Height;
// Draw border
if (!ThicknessUtil.IsZero(borders) && (brush = BorderBrush) != null)
{
var pen = GetPen(brush, borderStyle, borders.Left, dpi.DpiScaleX, useLayoutRounding);
var penThickness = pen.Thickness;
double x = penThickness * 0.5;
var rect = new Rect(x, x, width - penThickness, height - penThickness);
if (roundedCorners)
{
dc.DrawRoundedRectangle(null, pen, rect, outerCornerRadius, outerCornerRadius);
}
else
{
dc.DrawRectangle(null, pen, rect);
}
}
// Draw background in rectangle inside border.
if ((brush = Background) != null)
{
// Intialize background
Point ptTL, ptBR;
if (useLayoutRounding)
{
ptTL = new Point(UIElementUtil.RoundLayoutValue(borders.Left, dpi.DpiScaleX),
UIElementUtil.RoundLayoutValue(borders.Top, dpi.DpiScaleY));
ptBR = new Point(width - UIElementUtil.RoundLayoutValue(borders.Right, dpi.DpiScaleX),
height - UIElementUtil.RoundLayoutValue(borders.Bottom, dpi.DpiScaleY));
}
else
{
ptTL = new Point(borders.Left, borders.Top);
ptBR = new Point(width - borders.Right, height - borders.Bottom);
}
// Do not draw background if the borders are so large that they overlap.
if (ptBR.X > ptTL.X && ptBR.Y > ptTL.Y)
{
if (roundedCorners)
{
// Determine the inner edge radius
var innerCornerRadius = Math.Max(0.0, outerCornerRadius - borders.Top * 0.5);
dc.DrawRoundedRectangle(brush, null, new Rect(ptTL, ptBR), innerCornerRadius, innerCornerRadius);
}
else
{
dc.DrawRectangle(brush, null, new Rect(ptTL, ptBR));
}
}
}
}
private void ComplexRender(DrawingContext dc)
{
Brush brush;
var width = RenderSize.Width;
var height = RenderSize.Height;
//Draw border
if (!DoubleUtil.IsZero(width) && !DoubleUtil.IsZero(height) && (brush = BorderBrush) != null)
{
var useLayoutRounding = UseLayoutRounding;
var dpi = DpiUtil.GetDpi(this);
var borders = BorderThickness;
var borderStyle = BorderStyle;
var radius = CornerRadius;
double x, y;
// Left Line
if (!DoubleUtil.IsZero(borders.Left))
{
if (leftPenCache == null)
{
leftPenCache = GetPen(brush, borderStyle, borders.Left, dpi.DpiScaleX, useLayoutRounding);
}
x = leftPenCache.Thickness * 0.5;
dc.DrawLine(leftPenCache, new Point(x, radius.TopLeft), new Point(x, height - radius.BottomLeft));
}
// Top Line
if (!DoubleUtil.IsZero(borders.Top))
{
if (topPenCache == null)
{
topPenCache = GetPen(brush, borderStyle, borders.Top, dpi.DpiScaleY, useLayoutRounding);
}
y = topPenCache.Thickness * 0.5;
dc.DrawLine(topPenCache, new Point(radius.TopLeft, y), new Point(width - radius.TopRight, y));
}
// Right Line
if (!DoubleUtil.IsZero(borders.Right))
{
if (rightPenCache == null)
{
rightPenCache = GetPen(brush, borderStyle, borders.Right, dpi.DpiScaleX, useLayoutRounding);
}
x = width - rightPenCache.Thickness * 0.5;
dc.DrawLine(rightPenCache, new Point(x, radius.TopRight), new Point(x, height - radius.BottomRight));
}
// Bottom Line
if (!DoubleUtil.IsZero(borders.Bottom))
{
if (bottomPenCache == null)
{
bottomPenCache = GetPen(brush, borderStyle, borders.Bottom, dpi.DpiScaleY, useLayoutRounding);
}
y = height - bottomPenCache.Thickness * 0.5;
dc.DrawLine(bottomPenCache, new Point(radius.BottomLeft, y), new Point(width - radius.BottomRight, y));
}
// Draw Rounded
Pen pen;
if (upperLeftCache != null && (pen = GetMaxPen(leftPenCache, topPenCache)) != null)
{
dc.DrawGeometry(null, pen, upperLeftCache);
}
if (upperRightCache != null && (pen = GetMaxPen(topPenCache, rightPenCache)) != null)
{
dc.DrawGeometry(null, pen, upperRightCache);
}
if (lowerRightCache != null && (pen = GetMaxPen(rightPenCache, bottomPenCache)) != null)
{
dc.DrawGeometry(null, pen, lowerRightCache);
}
if (lowerLeftCache != null && (pen = GetMaxPen(bottomPenCache, leftPenCache)) != null)
{
dc.DrawGeometry(null, pen, lowerLeftCache);
}
}
// Draw background in rectangle inside border.
if (backgroundGeometryCache != null && (brush = Background) != null)
{
dc.DrawGeometry(brush, null, backgroundGeometryCache);
}
}
private Pen GetMaxPen(Pen pen1, Pen pen2)
{
if (pen2 == null || (pen1 != null && pen2.Thickness < pen1.Thickness))
{
return pen1;
}
return pen2;
}
private static StreamGeometry GenerateRoundedGeometry(Point startPoint, Point endPoint, Size size)
{
var streamGeometry = new StreamGeometry();
using (StreamGeometryContext sc = streamGeometry.Open())
{
sc.BeginFigure(startPoint, true, false);
sc.ArcTo(endPoint, size, 0, false, SweepDirection.Clockwise, true, false);
}
streamGeometry.Freeze();
return streamGeometry;
}
private static Pen GetPen(Brush brush, BorderStyle borderStyle, double thickness, double dpi, bool useLayoutRounding)
{
var pen = new Pen
{
Brush = brush,
DashCap = PenLineCap.Flat,
Thickness = useLayoutRounding ? UIElementUtil.RoundLayoutValue(thickness, dpi) : thickness,
};
switch (borderStyle)
{
case BorderStyle.Dotted:
pen.DashStyle = new DashStyle(new double[] { 1 }, 0d);
break;
case BorderStyle.Dashed:
pen.DashStyle = new DashStyle(new double[] { 4, 2 }, 0d);
break;
default:
break;
}
if (brush.IsFrozen)
{
pen.Freeze();
}
return pen;
}
#endregion
#region Private Structures Classes
private struct Radii
{
#region Fields
internal readonly Point LeftTop;
internal readonly Point LeftBottom;
internal readonly Point TopLeft;
internal readonly Point TopRight;
internal readonly Point RightTop;
internal readonly Point RightBottom;
internal readonly Point BottomRight;
internal readonly Point BottomLeft;
#endregion
internal Radii(Rect rect, CornerRadius radius, Thickness borders, bool outer)
{
var left = borders.Left * 0.5;
var top = borders.Top * 0.5;
var right = borders.Right * 0.5;
var bottom = borders.Bottom * 0.5;
LeftTop = new Point(0d, 0d);
LeftBottom = new Point(0d, rect.Height);
TopLeft = new Point(0d, 0d);
TopRight = new Point(rect.Width, 0d);
RightTop = new Point(rect.Width, 0d);
RightBottom = new Point(rect.Width, rect.Height);
BottomRight = new Point(rect.Width, rect.Height);
BottomLeft = new Point(0d, rect.Height);
if (outer)
{
LeftTop.X = left;
LeftBottom.X = left;
TopLeft.Y = top;
TopRight.Y = top;
RightTop.X -= right;
RightBottom.X -= right;
BottomLeft.Y -= bottom;
BottomRight.Y -= bottom;
if (!DoubleUtil.IsZero(radius.TopLeft))
{
TopLeft.X = radius.TopLeft; // + left;
LeftTop.Y = radius.TopLeft;// + top;
}
if (!DoubleUtil.IsZero(radius.TopRight))
{
RightTop.Y = radius.TopRight;// + top;
TopRight.X -= radius.TopRight;// + right;
}
if (!DoubleUtil.IsZero(radius.BottomRight))
{
BottomRight.X -= radius.BottomRight;// + right;
RightBottom.Y -= radius.BottomRight;// + bottom; ;
}
if (!DoubleUtil.IsZero(radius.BottomLeft))
{
LeftBottom.Y -= radius.BottomLeft; // + bottom;
BottomLeft.X = radius.BottomLeft;// + left;
}
} else
{
TopLeft.X = Math.Max(0.0, radius.TopLeft - left);
LeftTop.Y = Math.Max(0.0, radius.TopLeft - top);
RightTop.Y = Math.Max(0.0, radius.TopRight - top);
TopRight.X -= Math.Max(0.0, radius.TopRight - right);
BottomRight.X -= Math.Max(0.0, radius.BottomRight - right);
RightBottom.Y -= Math.Max(0.0, radius.BottomRight - bottom);
LeftBottom.Y -= Math.Max(0.0, radius.BottomLeft - bottom);
BottomLeft.X = Math.Max(0.0, radius.BottomLeft - left);
}
// check keypoints for overlap and resolve by partitioning corners according to
// the percentage of each one.
// top edge
if (TopLeft.X > TopRight.X)
{
var v = TopLeft.X / (TopLeft.X + rect.Width - TopRight.X) * rect.Width;
TopLeft.X = v;
TopRight.X = v;
}
// right edge
if (RightTop.Y > RightBottom.Y)
{
var v = RightTop.Y / (RightTop.Y + rect.Height - RightBottom.Y) * rect.Height;
RightTop.Y = v;
RightBottom.Y = v;
}
// bottom edge
if (BottomRight.X < BottomLeft.X)
{
var v = BottomLeft.X / (BottomLeft.X + rect.Width - BottomRight.X) * rect.Width;
BottomRight.X = v;
BottomLeft.X = v;
}
// left edge
if (LeftBottom.Y < LeftTop.Y)
{
var v = LeftTop.Y / (LeftTop.Y + rect.Height - LeftBottom.Y) * rect.Height;
LeftBottom.Y = v;
LeftTop.Y = v;
}
// Apply offset
var offset = new Vector(rect.TopLeft.X, rect.TopLeft.Y);
LeftTop += offset;
LeftBottom += offset;
TopRight += offset;
TopLeft += offset;
RightTop += offset;
RightBottom += offset;
BottomRight += offset;
BottomLeft += offset;
}
}
#endregion Private Structures Classes
}

View File

@@ -0,0 +1,39 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
using System.Windows.Controls;
public class AntdContentControl : ContentControl
{
public static readonly DependencyProperty ContentCharacterCasingProperty =
DependencyProperty.Register("ContentCharacterCasing", typeof(CharacterCasing), typeof(AntdContentControl),
new FrameworkPropertyMetadata(CharacterCasing.Normal, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsMeasure),
value => CharacterCasing.Normal <= (CharacterCasing)value && (CharacterCasing)value <= CharacterCasing.Upper);
/// <summary>
/// Character casing of the Content
/// </summary>
public CharacterCasing ContentCharacterCasing
{
get { return (CharacterCasing)GetValue(ContentCharacterCasingProperty); }
set { SetValue(ContentCharacterCasingProperty, value); }
}
public static readonly DependencyProperty RecognizesAccessKeyProperty =
DependencyProperty.Register("RecognizesAccessKey", typeof(bool), typeof(AntdContentControl), new FrameworkPropertyMetadata(false));
/// <summary>
/// Determine if the inner ContentPresenter should use AccessText in its style
/// </summary>
public bool RecognizesAccessKey
{
get { return (bool)GetValue(RecognizesAccessKeyProperty); }
set { SetValue(RecognizesAccessKeyProperty, value); }
}
static AntdContentControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AntdContentControl), new FrameworkPropertyMetadata(typeof(AntdContentControl)));
}
}
}

View File

@@ -0,0 +1,718 @@
using System;
using System.Collections;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using AntDesign.WPF.Contracts;
using AntDesign.WPF.Microsoft.Windows.Shell;
using AntDesign.WPF.Win32;
using Standard;
namespace AntDesign.WPF.Controls
{
[TemplatePart(Name = PART_TitleBarThumb, Type = typeof(Thumb))]
[TemplatePart(Name = PART_TitleBar, Type = typeof(UIElement))]
[TemplatePart(Name = PART_Icon, Type = typeof(FrameworkElement))]
[TemplatePart(Name = PART_LeftWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_Title, Type = typeof(UIElement))]
[TemplatePart(Name = PART_RightWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_WindowButtons, Type = typeof(WindowButtons))]
public class AntdWindow : Window
{
#region Fields
private const string PART_TitleBarThumb = "PART_TitleBarThumb";
private const string PART_TitleBar = "PART_TitleBar";
private const string PART_Icon = "PART_Icon";
private const string PART_LeftWindowCommands = "PART_LeftWindowCommands";
private const string PART_Title = "PART_Title";
private const string PART_RightWindowCommands = "PART_RightWindowCommands";
private const string PART_WindowButtons = "PART_WindowButtons";
private Thumb titleBarThumb;
private UIElement titleBar;
private FrameworkElement icon;
internal ContentPresenter leftWindowCommands;
private UIElement title;
internal ContentPresenter rightWindowCommands;
internal ContentPresenter windowButtons;
#endregion
#region Properties
public static readonly DependencyProperty IgnoreTaskbarProperty =
DependencyProperty.Register("IgnoreTaskbar", typeof(bool), typeof(AntdWindow), new PropertyMetadata(false));
/// <summary>
/// Gets/sets whether the window will ignore (and overlap) the taskbar when maximized.
/// </summary>
public bool IgnoreTaskbar
{
get { return (bool)GetValue(IgnoreTaskbarProperty); }
set { SetValue(IgnoreTaskbarProperty, value); }
}
public static readonly DependencyProperty ShowSystemMenuProperty =
DependencyProperty.Register("ShowSystemMenu", typeof(bool), typeof(AntdWindow), new PropertyMetadata(false));
/// <summary>
/// Gets/sets if the the system menu should popup on right click.
/// </summary>
public bool ShowSystemMenu
{
get { return (bool)GetValue(ShowSystemMenuProperty); }
set { SetValue(ShowSystemMenuProperty, value); }
}
public static readonly DependencyProperty IsDraggableProperty =
DependencyProperty.Register("IsDraggable", typeof(bool), typeof(AntdWindow), new PropertyMetadata(true));
/// <summary>
/// Gets/sets if the the allow drag window
/// </summary>
public bool IsDraggable
{
get { return (bool)GetValue(IsDraggableProperty); }
set { SetValue(IsDraggableProperty, value); }
}
public static readonly DependencyProperty UseNoneWindowStyleProperty =
DependencyProperty.Register("UseNoneWindowStyle", typeof(bool), typeof(AntdWindow), new PropertyMetadata(false, OnUseNoneWindowStyleChanged));
/// <summary>
/// Gets/sets whether the WindowStyle is None or not.
/// </summary>
public bool UseNoneWindowStyle
{
get { return (bool)GetValue(UseNoneWindowStyleProperty); }
set { SetValue(UseNoneWindowStyleProperty, value); }
}
private static void OnUseNoneWindowStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as AntdWindow).ToggleNoneWindowStyle((bool)e.NewValue);
}
}
private void ToggleNoneWindowStyle(bool useNoneWindowStyle)
{
SetVisibiltyForTitleBarElements(!useNoneWindowStyle && TitleBarHeight > 0);
}
protected IntPtr CriticalHandle
{
get
{
var value = typeof(Window).GetProperty("CriticalHandle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this, new object[0]);
return (IntPtr)value;
}
}
public static readonly DependencyProperty TitleBarHeightProperty =
DependencyProperty.Register("TitleBarHeight", typeof(double), typeof(AntdWindow), new PropertyMetadata(30d, OnTitleBarHeightChanged));
/// <summary>
/// Gets/sets the TitleBar height.
/// </summary>
public double TitleBarHeight
{
get { return (double)GetValue(TitleBarHeightProperty); }
set { SetValue(TitleBarHeightProperty, value); }
}
private static void OnTitleBarHeightChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var window = (AntdWindow)dependencyObject;
if (e.NewValue != e.OldValue)
{
window.SetVisibiltyForTitleBarElements((int)e.NewValue > 0);
}
}
public static readonly DependencyProperty TitleBarForegroundProperty =
DependencyProperty.Register("TitleBarForeground", typeof(Brush), typeof(AntdWindow));
/// <summary>
/// Gets/sets the foreground brush of the title bar.
/// </summary>
public Brush TitleBarForeground
{
get { return (Brush)GetValue(TitleBarForegroundProperty); }
set { SetValue(TitleBarForegroundProperty, value); }
}
public static readonly DependencyProperty TitleBarBackgroundProperty =
DependencyProperty.Register("TitleBarBackground", typeof(Brush), typeof(AntdWindow), new PropertyMetadata(Brushes.Transparent));
/// <summary>
/// Gets/sets the background brush of the title bar.
/// </summary>
public Brush TitleBarBackground
{
get { return (Brush)GetValue(TitleBarBackgroundProperty); }
set { SetValue(TitleBarBackgroundProperty, value); }
}
public static readonly DependencyProperty ShowIconProperty =
DependencyProperty.Register("ShowIcon", typeof(bool), typeof(AntdWindow), new PropertyMetadata(true, OnShowIconChanged));
/// <summary>
/// Gets/sets whether the titlebar icon is visible or not.
/// </summary>
public bool ShowIcon
{
get { return (bool)GetValue(ShowIconProperty); }
set { SetValue(ShowIconProperty, value); }
}
private static void OnShowIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as AntdWindow).SetVisibiltyForIcon();
}
}
public static readonly DependencyProperty IconTemplateProperty =
DependencyProperty.Register("IconTemplate", typeof(DataTemplate), typeof(AntdWindow), new PropertyMetadata(null));
/// <summary>
/// Gets/sets the icon content template to show a custom icon.
/// </summary>
public DataTemplate IconTemplate
{
get { return (DataTemplate)GetValue(IconTemplateProperty); }
set { SetValue(IconTemplateProperty, value); }
}
public static readonly DependencyProperty TitleCharacterCasingProperty =
DependencyProperty.Register("TitleCharacterCasing", typeof(CharacterCasing), typeof(AntdWindow),
new FrameworkPropertyMetadata(CharacterCasing.Normal, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsMeasure),
value => CharacterCasing.Normal <= (CharacterCasing)value && (CharacterCasing)value <= CharacterCasing.Upper);
/// <summary>
/// Character casing of the title
/// </summary>
public CharacterCasing TitleCharacterCasing
{
get { return (CharacterCasing)GetValue(TitleCharacterCasingProperty); }
set { SetValue(TitleCharacterCasingProperty, value); }
}
public static readonly DependencyProperty TitleAlignmentProperty =
DependencyProperty.Register("TitleAlignment", typeof(HorizontalAlignment), typeof(AntdWindow), new PropertyMetadata(HorizontalAlignment.Stretch, OnTitleAlignmentChanged));
public HorizontalAlignment TitleAlignment
{
get { return (HorizontalAlignment)GetValue(TitleAlignmentProperty); }
set { SetValue(TitleAlignmentProperty, value); }
}
private static void OnTitleAlignmentChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (dependencyObject is AntdWindow window)
{
window.SizeChanged -= window.OnSizeChanged;
if ((HorizontalAlignment)e.NewValue == HorizontalAlignment.Center)
{
window.SizeChanged += window.OnSizeChanged;
}
}
}
public static readonly DependencyProperty TitleTemplateProperty =
DependencyProperty.Register("TitleTemplate", typeof(DataTemplate), typeof(AntdWindow), new PropertyMetadata(null));
/// <summary>
/// Gets/sets the title content template to show a custom title.
/// </summary>
public DataTemplate TitleTemplate
{
get { return (DataTemplate)GetValue(TitleTemplateProperty); }
set { SetValue(TitleTemplateProperty, value); }
}
public static readonly DependencyProperty LeftWindowCommandsProperty =
DependencyProperty.Register("LeftWindowCommands", typeof(WindowCommands), typeof(AntdWindow), new PropertyMetadata(null, UpdateLogicalChilds));
/// <summary>
/// Gets/sets the left window commands that hosts the user commands.
/// </summary>
public WindowCommands LeftWindowCommands
{
get { return (WindowCommands)GetValue(LeftWindowCommandsProperty); }
set { SetValue(LeftWindowCommandsProperty, value); }
}
public static readonly DependencyProperty RightWindowCommandsProperty =
DependencyProperty.Register("RightWindowCommands", typeof(WindowCommands), typeof(AntdWindow), new PropertyMetadata(null, UpdateLogicalChilds));
/// <summary>
/// Gets/sets the right window commands that hosts the user commands.
/// </summary>
public WindowCommands RightWindowCommands
{
get { return (WindowCommands)GetValue(RightWindowCommandsProperty); }
set { SetValue(RightWindowCommandsProperty, value); }
}
public static readonly DependencyProperty WindowButtonsProperty =
DependencyProperty.Register("WindowButtons", typeof(WindowButtons), typeof(AntdWindow), new PropertyMetadata(null, UpdateLogicalChilds));
/// <summary>
/// Gets/sets the window button commands that hosts the min/max/close commands.
/// </summary>
public WindowButtons WindowButtons
{
get { return (WindowButtons)GetValue(WindowButtonsProperty); }
set { SetValue(WindowButtonsProperty, value); }
}
private static void UpdateLogicalChilds(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (!(dependencyObject is AntdWindow window))
{
return;
}
if (e.OldValue is FrameworkElement oldChild)
{
window.RemoveLogicalChild(oldChild);
}
if (e.NewValue is FrameworkElement newChild)
{
window.AddLogicalChild(newChild);
// Yes, that's crazy. But we must do this to enable all possible scenarios for setting DataContext
// in a Window. Without set the DataContext at this point it can happen that e.g. a Flyout
// doesn't get the same DataContext.
// So now we can type
//
// InitializeComponent();
// DataContext = new MainViewModel();
//
// or
//
// DataContext = new MainViewModel();
// InitializeComponent();
//
newChild.DataContext = window.DataContext;
}
}
#endregion
#region Constructors
static AntdWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AntdWindow), new FrameworkPropertyMetadata(typeof(AntdWindow)));
}
public AntdWindow()
{
//SetResourceReference(StyleProperty, typeof(AntdWindow));
DataContextChanged += OnDataContextChanged;
}
#endregion
#region LogicalTree
protected override IEnumerator LogicalChildren
{
get
{
// cheat, make a list with all logical content and return the enumerator
ArrayList children = new ArrayList { Content };
if (LeftWindowCommands != null)
{
children.Add(LeftWindowCommands);
}
if (RightWindowCommands != null)
{
children.Add(RightWindowCommands);
}
if (WindowButtons != null)
{
children.Add(WindowButtons);
}
return children.GetEnumerator();
}
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
titleBarThumb = GetTemplateChild(PART_TitleBarThumb) as Thumb;
titleBar = GetTemplateChild(PART_TitleBar) as UIElement;
icon = GetTemplateChild(PART_Icon) as FrameworkElement;
title = GetTemplateChild(PART_Title) as UIElement;
leftWindowCommands = GetTemplateChild(PART_LeftWindowCommands) as ContentPresenter;
rightWindowCommands = GetTemplateChild(PART_RightWindowCommands) as ContentPresenter;
windowButtons = GetTemplateChild(PART_WindowButtons) as ContentPresenter;
if (LeftWindowCommands == null)
LeftWindowCommands = new WindowCommands();
if (RightWindowCommands == null)
RightWindowCommands = new WindowCommands();
if (WindowButtons == null)
WindowButtons = new WindowButtons();
LeftWindowCommands.ParentWindow = this;
RightWindowCommands.ParentWindow = this;
WindowButtons.ParentWindow = this;
var windowChrome = GetValue(WindowChrome.WindowChromeProperty) as WindowChrome;
if (windowChrome != null)
{
BindingOperations.SetBinding(
windowChrome,
WindowChrome.IgnoreTaskbarProperty,
new Binding { Path = new PropertyPath(IgnoreTaskbarProperty), Source = this, Mode = BindingMode.OneWay }
);
}
ToggleNoneWindowStyle(UseNoneWindowStyle);
}
#endregion
#region Private Methods
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// add these controls to the window with AddLogicalChild method.
// This has the side effect that the DataContext doesn't update, so do this now here.
if (LeftWindowCommands != null) LeftWindowCommands.DataContext = DataContext;
if (RightWindowCommands != null) RightWindowCommands.DataContext = DataContext;
if (WindowButtons != null) WindowButtons.DataContext = DataContext;
}
private void OnSizeChanged(object sender, RoutedEventArgs e)
{
// this all works only for centered title
if (TitleAlignment != HorizontalAlignment.Center) return;
// Half of this Window
var halfDistance = ActualWidth / 2;
// Distance between center and left/right
var distanceToCenter = title.DesiredSize.Width / 2;
// Distance between right edge from LeftWindowCommands to left window side
var distanceFromLeft = icon.ActualWidth + LeftWindowCommands.ActualWidth;
// Distance between left edge from RightWindowCommands to right window side
var distanceFromRight = WindowButtons.ActualWidth + RightWindowCommands.ActualWidth;
// Margin
const double horizontalMargin = 5.0;
var dLeft = distanceFromLeft + distanceToCenter + horizontalMargin;
var dRight = distanceFromRight + distanceToCenter + horizontalMargin;
// TODO They should also change when the layout changes.
if ((dLeft < halfDistance) && (dRight < halfDistance))
{
Grid.SetColumn(title, 0);
Grid.SetColumnSpan(title, 5);
}
else
{
Grid.SetColumn(title, 2);
Grid.SetColumnSpan(title, 1);
}
}
private void SetVisibiltyForTitleBarElements(bool visible)
{
var value = visible ? Visibility.Visible : Visibility.Collapsed;
titleBar?.SetCurrentValue(VisibilityProperty, value);
title?.SetCurrentValue(VisibilityProperty, value);
SetVisibiltyForIcon();
leftWindowCommands?.SetValue(VisibilityProperty, value);
rightWindowCommands?.SetValue(VisibilityProperty, value);
windowButtons?.SetCurrentValue(VisibilityProperty, value);
SetWindowEvents();
}
private void SetVisibiltyForIcon()
{
if (icon == null) return;
icon.Visibility = ShowIcon && !UseNoneWindowStyle && TitleBarHeight > 0 ? Visibility.Visible : Visibility.Collapsed;
}
private void SetWindowEvents()
{
// clear all event handlers first
ClearWindowEvents();
// set mouse down/up for icon
if (icon != null && icon.Visibility == Visibility.Visible)
{
icon.MouseLeftButtonDown += OnIconMouseLeftButtonDown;
icon.MouseRightButtonUp += OnThumbMouseRightButtonUp;
}
if (titleBarThumb != null)
{
titleBarThumb.PreviewMouseLeftButtonUp += OnThumbPreviewMouseLeftButtonUp;
titleBarThumb.DragDelta += OnThumbDragDelta;
titleBarThumb.MouseDoubleClick += OnThumbMouseDoubleClick;
titleBarThumb.MouseRightButtonUp += OnThumbMouseRightButtonUp;
}
var thumbContentControl = title as IThumb;
if (thumbContentControl != null)
{
thumbContentControl.PreviewMouseLeftButtonUp += OnThumbPreviewMouseLeftButtonUp;
thumbContentControl.DragDelta += OnThumbDragDelta;
thumbContentControl.MouseDoubleClick += OnThumbMouseDoubleClick;
thumbContentControl.MouseRightButtonUp += OnThumbMouseRightButtonUp;
}
// handle size if we have a Grid for the title (e.g. clean window have a centered title)
if (title != null && TitleAlignment == HorizontalAlignment.Center)
{
SizeChanged += OnSizeChanged;
}
}
private void ClearWindowEvents()
{
// clear all event handlers first:
if (titleBarThumb != null)
{
titleBarThumb.PreviewMouseLeftButtonUp -= OnThumbPreviewMouseLeftButtonUp;
titleBarThumb.DragDelta -= OnThumbDragDelta;
titleBarThumb.MouseDoubleClick -= OnThumbMouseDoubleClick;
titleBarThumb.MouseRightButtonUp -= OnThumbMouseRightButtonUp;
}
var thumbContentControl = title as IThumb;
if (thumbContentControl != null)
{
thumbContentControl.PreviewMouseLeftButtonUp -= OnThumbPreviewMouseLeftButtonUp;
thumbContentControl.DragDelta -= OnThumbDragDelta;
thumbContentControl.MouseDoubleClick -= OnThumbMouseDoubleClick;
thumbContentControl.MouseRightButtonUp -= OnThumbMouseRightButtonUp;
}
if (icon != null)
{
icon.MouseLeftButtonDown -= OnIconMouseLeftButtonDown;
icon.MouseRightButtonUp -= OnThumbMouseRightButtonUp;
}
SizeChanged -= OnSizeChanged;
}
private void OnIconMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!ShowSystemMenu) return;
if (e.ClickCount == 2)
{
Close();
} else
{
System.Windows.SystemCommands.ShowSystemMenu(this, PointToScreen(new Point(0, TitleBarHeight)));
}
}
private void OnThumbPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DoThumbPreviewMouseLeftButtonUp(this, e);
}
private void OnThumbDragDelta(object sender, DragDeltaEventArgs e)
{
DoThumbDragDelta(sender as IThumb, this, e);
}
private void OnThumbMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
DoThumbMouseDoubleClick(this, e);
}
private void OnThumbMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
DoThumbMouseRightButtonUp(this, e);
}
internal static void DoThumbPreviewMouseLeftButtonUp(AntdWindow window, MouseButtonEventArgs e)
{
if (e.Source == e.OriginalSource)
{
Mouse.Capture(null);
}
}
internal static void DoThumbDragDelta(IThumb thumb, AntdWindow window, DragDeltaEventArgs e)
{
if (thumb == null)
{
throw new ArgumentNullException(nameof(thumb));
}
if (window == null)
{
throw new ArgumentNullException(nameof(window));
}
// drag only if IsDraggable is set to true
if (!window.IsDraggable ||
(!(Math.Abs(e.HorizontalChange) > 2) && !(Math.Abs(e.VerticalChange) > 2)))
{
return;
}
// tage from DragMove internal code
window.VerifyAccess();
//var cursorPos = WinApiHelper.GetPhysicalCursorPos();
// if the window is maximized dragging is only allowed on title bar (also if not visible)
var maximized = window.WindowState == WindowState.Maximized;
var isMouseOnTitlebar = Mouse.GetPosition(thumb).Y <= window.TitleBarHeight && window.TitleBarHeight > 0;
if (!isMouseOnTitlebar && maximized) return;
#pragma warning disable 618
// for the touch usage
UnsafeNativeMethods.ReleaseCapture();
#pragma warning restore 618
if (maximized)
{
//var cursorXPos = cursorPos.x;
EventHandler windowOnStateChanged = null;
windowOnStateChanged = (sender, args) =>
{
//window.Top = 2;
//window.Left = Math.Max(cursorXPos - window.RestoreBounds.Width / 2, 0);
window.StateChanged -= windowOnStateChanged;
if (window.WindowState == WindowState.Normal)
{
Mouse.Capture(thumb, CaptureMode.Element);
}
};
window.StateChanged += windowOnStateChanged;
}
var criticalHandle = window.CriticalHandle;
// DragMove works too
// window.DragMove();
// instead this 2 lines
#pragma warning disable 618
NativeMethods.SendMessage(criticalHandle, WM.SYSCOMMAND, (IntPtr)SC.MOUSEMOVE, IntPtr.Zero);
NativeMethods.SendMessage(criticalHandle, WM.LBUTTONUP, IntPtr.Zero, IntPtr.Zero);
#pragma warning restore 618
}
/// <summary>
/// Change window state on MouseDoubleClick
/// </summary>
/// <param name="window"></param>
/// <param name="e"></param>
internal static void DoThumbMouseDoubleClick(AntdWindow window, MouseButtonEventArgs e)
{
// restore/maximize only with left button
if (e.ChangedButton == MouseButton.Left)
{
// we can maximize or restore the window if the title bar height is set (also if title bar is hidden)
var canResize = window.ResizeMode == ResizeMode.CanResizeWithGrip || window.ResizeMode == ResizeMode.CanResize;
var mousePos = Mouse.GetPosition(window);
var isMouseOnTitlebar = mousePos.Y <= window.TitleBarHeight && window.TitleBarHeight > 0;
if (canResize && isMouseOnTitlebar)
{
if (window.WindowState == WindowState.Normal)
{
System.Windows.SystemCommands.MaximizeWindow(window);
}
else
{
System.Windows.SystemCommands.RestoreWindow(window);
}
e.Handled = true;
}
}
}
/// <summary>
/// Show system menu on MouseRightButtonUp
/// </summary>
/// <param name="window"></param>
/// <param name="e"></param>
internal static void DoThumbMouseRightButtonUp(AntdWindow window, MouseButtonEventArgs e)
{
if (window.ShowSystemMenu)
{
// show menu only if mouse pos is on title bar or if we have a window with none style and no title bar
var mousePos = e.GetPosition(window);
if ((mousePos.Y <= window.TitleBarHeight && window.TitleBarHeight > 0) || (window.UseNoneWindowStyle && window.TitleBarHeight <= 0))
{
System.Windows.SystemCommands.ShowSystemMenu(window, window.PointToScreen(mousePos));
}
}
}
/// <summary>
/// Gets the template child with the given name.
/// </summary>
/// <typeparam name="T">The interface type inheirted from DependencyObject.</typeparam>
/// <param name="name">The name of the template child.</param>
internal T GetPart<T>(string name) where T : class
{
return GetTemplateChild(name) as T;
}
/// <summary>
/// Gets the template child with the given name.
/// </summary>
/// <param name="name">The name of the template child.</param>
internal DependencyObject GetPart(string name)
{
return GetTemplateChild(name);
}
#endregion
}
}

View File

@@ -0,0 +1,256 @@
namespace AntDesign.WPF.Controls;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
/// <summary>
/// Avatars can be used to represent people or objects. It supports images, Icons, or letters.
/// </summary>
[ContentProperty("Text")]
[TemplatePart(Name = "PART_Content", Type = typeof(ContentPresenter))]
public class Avatar : Control
{
#region Fields
private const string PART_Content = "PART_Content";
private ContentPresenter contentPresenter;
#endregion
#region Properties
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(string), typeof(Avatar), new PropertyMetadata(null, OnContentChanged));
/// <summary>
/// Gets/sets the icon type for an icon avatar.
/// </summary>
public string Icon
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Avatar).SetContent(true);
}
public static readonly DependencyProperty ShapeProperty =
DependencyProperty.Register("Shape", typeof(Shapes), typeof(Avatar), new PropertyMetadata(Shapes.Circle));
/// <summary>
/// Gets/sets the shape of avatar.
/// </summary>
public Shapes Shape
{
get { return (Shapes)GetValue(ShapeProperty); }
set { SetValue(ShapeProperty, value); }
}
public static readonly DependencyProperty SizeProperty =
DependencyProperty.Register("Size", typeof(Sizes?), typeof(Avatar), new PropertyMetadata(null, OnSizeChanged));
/// <summary>
/// Gets/sets the size of the avatar.
/// </summary>
public Sizes? Size
{
get { return (Sizes?)GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
private static void OnSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var avatar = d as Avatar;
var newValue = (Sizes?)e.NewValue;
var oldValue = (Sizes?)e.OldValue;
if (newValue.HasValue && newValue.Value >= 0)
{
var size = (double)newValue.Value;
avatar.SetValue(WidthProperty, size);
avatar.SetValue(HeightProperty, size);
avatar.SetValue(FontSizeProperty, size / 2);
}
else if (oldValue.HasValue && oldValue.Value >= 0)
{
avatar.ClearValue(WidthProperty);
avatar.ClearValue(HeightProperty);
avatar.ClearValue(FontSizeProperty);
}
}
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ImageSource), typeof(Avatar), new PropertyMetadata(null, OnContentChanged));
/// <summary>
/// Gets/sets the ImageSource for an image avatar.
/// </summary>
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Avatar), new PropertyMetadata(string.Empty, OnContentChanged));
/// <summary>
/// Gets/sets the text for an text avatar.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty AlternativeProperty =
DependencyProperty.Register("Alternative", typeof(string), typeof(Avatar), new PropertyMetadata(string.Empty));
/// <summary>
/// Gets/sets the alternative text describing the image.
/// </summary>
public string Alternative
{
get { return (string)GetValue(AlternativeProperty); }
set { SetValue(AlternativeProperty, value); }
}
public static readonly DependencyProperty IsImageProperty =
DependencyProperty.Register("IsImage", typeof(bool), typeof(Avatar), new PropertyMetadata(false));
/// <summary>
/// Gets the current avatar type as an image.
/// </summary>
public bool IsImage
{
get { return (bool)GetValue(IsImageProperty); }
private set { SetValue(IsImageProperty, value); }
}
#endregion
#region Constructors
static Avatar()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Avatar), new FrameworkPropertyMetadata(typeof(Avatar)));
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
contentPresenter = GetTemplateChild(PART_Content) as ContentPresenter;
SetContent(true);
}
#endregion
#region Private Methods
private void SetContent(bool imageExist)
{
if (contentPresenter == null) return;
var content = contentPresenter.Content;
// Clear Event
if (content is Image img)
{
ClearValue(IsImageProperty);
img.ImageFailed -= OnImageFailed;
}
else if (content is TextBlock block)
{
SizeChanged -= OnTextSizeChanged;
block.SizeChanged -= OnTextSizeChanged;
}
if (Source != null && imageExist)
{
if (content is not Image)
{
content = new Image();
}
SetCurrentValue(IsImageProperty, true);
var image = (Image)content;
image.Source = Source;
image.ImageFailed += OnImageFailed;
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
}
else if (Icon != null)
{
if (content is not Controls.Icon icon)
{
content = new Icon();
}
((Icon)content).Type = Icon;
}
else
{
var text = string.IsNullOrEmpty(Text) ? (imageExist ? string.Empty : Alternative) : Text;
if (content is not TextBlock)
{
content = new TextBlock();
}
var textblock = (TextBlock)content;
SizeChanged += OnTextSizeChanged;
textblock.SizeChanged += OnTextSizeChanged;
textblock.Text = text;
textblock.RenderTransformOrigin = new Point(0.5, 0.5);
}
// 引用传递对 Null 无效
contentPresenter.Content = content;
}
private void OnImageFailed(object sender, ExceptionRoutedEventArgs e)
{
SetContent(false);
}
/// <summary>
/// Autoset Font Size
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTextSizeChanged(object sender, SizeChangedEventArgs e)
{
if (contentPresenter != null && contentPresenter.Content is TextBlock textBlock)
{
var childrenWidth = textBlock.ActualWidth;
var width = ActualWidth - 8;
var scale = 1d;
var left = 0d;
if (width < childrenWidth)
{
scale = width / childrenWidth;
left = ActualWidth / 2 - childrenWidth / 2;
}
textBlock.Margin = new Thickness(left, 0d, left, 0d);
textBlock.RenderTransform = new ScaleTransform(scale, scale);
}
}
#endregion
}

View File

@@ -0,0 +1,223 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using WindowSize = System.Windows.Size;
using ContentControlBase = System.Windows.Controls.ContentControl;
namespace AntDesign.WPF.Controls;
[TemplatePart(Name = PART_BadgeContainer, Type = typeof(FrameworkElement))]
[TemplatePart(Name = PART_Count, Type = typeof(ContentPresenter))]
public class Badge : ContentControlBase
{
#region Fields
private const string PART_BadgeContainer = "PART_BadgeContainer";
private const string PART_Count = "PART_Count";
private FrameworkElement badgeContainer;
private ContentPresenter count;
#endregion
#region Properties
public static readonly DependencyProperty CountProperty =
DependencyProperty.Register("Count", typeof(object), typeof(Badge), new PropertyMetadata(null, OnCountChanged));
/// <summary>
/// Gets/sets number to show in badge
/// </summary>
public object Count
{
get { return (object)GetValue(CountProperty); }
set { SetValue(CountProperty, value); }
}
private static void OnCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Badge).ApplyCount();
}
private void ApplyCount()
{
if (count == null) return;
var content = Count;
if (Count is string)
{
try
{
var d = int.Parse(Count as string);
if (d > OverflowCount)
{
content = OverflowCount + "+";
}
}
catch { } // Swallow the error, it may be normal
}
count.Content = content;
}
public static readonly DependencyProperty DotProperty =
DependencyProperty.Register("Dot", typeof(bool), typeof(Badge), new PropertyMetadata(false));
/// <summary>
/// Gets/sets whether to display a red dot instead of count
/// </summary>
public bool Dot
{
get { return (bool)GetValue(DotProperty); }
set { SetValue(DotProperty, value); }
}
public static readonly DependencyProperty OffsetProperty =
DependencyProperty.Register("Offset", typeof(Point?), typeof(Badge), new PropertyMetadata(null));
public Point? Offset
{
get { return (Point?)GetValue(OffsetProperty); }
set { SetValue(OffsetProperty, value); }
}
public static readonly DependencyProperty OverflowCountProperty =
DependencyProperty.Register("OverflowCount", typeof(int), typeof(Badge), new PropertyMetadata(99, OnCountChanged));
/// <summary>
/// Gets/sets max count to show
/// </summary>
public int OverflowCount
{
get { return (int)GetValue(OverflowCountProperty); }
set { SetValue(OverflowCountProperty, value); }
}
public static readonly DependencyProperty ShowZeroProperty =
DependencyProperty.Register("ShowZero", typeof(bool), typeof(Badge), new PropertyMetadata(false));
/// <summary>
/// Gets/sets whether to show badge when count is zero
/// </summary>
public bool ShowZero
{
get { return (bool)GetValue(ShowZeroProperty); }
set { SetValue(ShowZeroProperty, value); }
}
public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(BadgeStatus?), typeof(Badge), new PropertyMetadata(null));
/// <summary>
/// Gets/sets badge as a status dot
/// </summary>
public BadgeStatus? Status
{
get { return (BadgeStatus?)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Badge), new PropertyMetadata(string.Empty));
/// <summary>
/// Gets/sets the text of the status dot. valid with StatusProperty set
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty BadgeHeightProperty =
DependencyProperty.Register("BadgeHeight", typeof(double), typeof(Badge), new PropertyMetadata(default(double)));
public double BadgeHeight
{
get { return (double)GetValue(BadgeHeightProperty); }
set { SetValue(BadgeHeightProperty, value); }
}
public static readonly DependencyProperty BadgeForegroundProperty =
DependencyProperty.Register("BadgeForeground", typeof(Brush), typeof(Badge), new PropertyMetadata(default(Brush)));
public Brush BadgeForeground
{
get { return (Brush)GetValue(BadgeForegroundProperty); }
set { SetValue(BadgeForegroundProperty, value); }
}
public static readonly DependencyProperty BadgeBackgroundProperty =
DependencyProperty.Register("BadgeBackground", typeof(Brush), typeof(Badge), new PropertyMetadata(default(Brush)));
public Brush BadgeBackground
{
get { return (Brush)GetValue(BadgeBackgroundProperty); }
set { SetValue(BadgeBackgroundProperty, value); }
}
#endregion
#region Constructors
static Badge()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(typeof(Badge)));
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
badgeContainer = GetTemplateChild(PART_BadgeContainer) as FrameworkElement;
count = GetTemplateChild(PART_Count) as ContentPresenter;
ApplyCount();
}
protected override WindowSize ArrangeOverride(WindowSize arrangeBounds)
{
var result = base.ArrangeOverride(arrangeBounds);
if (badgeContainer == null) return result;
var desiredSize = badgeContainer.DesiredSize;
// System.Console.WriteLine(desiredSize);
// if ((desiredSize.Width <= 0.0 || desiredSize.Height <= 0.0))
//var containerDesiredSize = _badgeContainer.DesiredSize;
//if ((containerDesiredSize.Width <= 0.0 || containerDesiredSize.Height <= 0.0)
// && !double.IsNaN(_badgeContainer.ActualWidth) && !double.IsInfinity(_badgeContainer.ActualWidth)
// && !double.IsNaN(_badgeContainer.ActualHeight) && !double.IsInfinity(_badgeContainer.ActualHeight))
//{
// containerDesiredSize = new Size(_badgeContainer.ActualWidth, _badgeContainer.ActualHeight);
//}
var h = 0 - desiredSize.Width / 2;
var v = 0 - desiredSize.Height / 2;
// badgeContainer.Margin = new Thickness(0);
// badgeContainer.Margin = new Thickness(h, v, h, v);
return result;
}
#endregion
}
public enum BadgeStatus : byte
{
Success, Processing, Default, Error, Warning
}

View File

@@ -0,0 +1,7 @@
namespace AntDesign.WPF.Controls
{
public enum BorderStyle : byte
{
Solid, Dotted, Dashed
}
}

View File

@@ -0,0 +1,16 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
using System.Windows.Controls.Primitives;
/// <summary>
/// CheckableTag works like Checkbox, click it to toggle checked.
/// </summary>
public class CheckableTag : ToggleButton
{
static CheckableTag()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckableTag), new FrameworkPropertyMetadata(typeof(CheckableTag)));
}
}
}

View File

@@ -0,0 +1,9 @@
namespace AntDesign.WPF.Controls
{
using System;
public class ClosingWindowEventHandlerArgs : EventArgs
{
public bool Cancelled { get; set; }
}
}

View File

@@ -0,0 +1,80 @@
using System.Windows;
using System.Windows.Controls;
namespace AntDesign.WPF.Controls
{
public class CodeBox : AntdContentControl
{
#region Properties
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(CodeBox), new PropertyMetadata(string.Empty));
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleTemplateProperty =
DependencyProperty.Register("TitleTemplate", typeof(DataTemplate), typeof(CodeBox), new PropertyMetadata(null));
public DataTemplate TitleTemplate
{
get { return (DataTemplate)GetValue(TitleTemplateProperty); }
set { SetValue(TitleTemplateProperty, value); }
}
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(string), typeof(CodeBox), new PropertyMetadata(string.Empty));
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
public static readonly DependencyProperty DescriptionTemplateProperty =
DependencyProperty.Register("DescriptionTemplate", typeof(DataTemplate), typeof(CodeBox), new PropertyMetadata(null));
public DataTemplate DescriptionTemplate
{
get { return (DataTemplate)GetValue(DescriptionTemplateProperty); }
set { SetValue(DescriptionTemplateProperty, value); }
}
public static readonly DependencyProperty CodeProperty =
DependencyProperty.Register("Code", typeof(string), typeof(CodeBox), new PropertyMetadata(string.Empty));
public string Code
{
get { return (string)GetValue(CodeProperty); }
set { SetValue(CodeProperty, value); }
}
public static readonly DependencyProperty CodeTemplateProperty =
DependencyProperty.Register("CodeTemplate", typeof(DataTemplate), typeof(CodeBox), new PropertyMetadata(null));
public DataTemplate CodeTemplate
{
get { return (DataTemplate)GetValue(CodeTemplateProperty); }
set { SetValue(CodeTemplateProperty, value); }
}
#endregion
#region Constructors
static CodeBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CodeBox), new FrameworkPropertyMetadata(typeof(CodeBox)));
}
//public CodeBox()
//{
// SetResourceReference(StyleProperty, typeof(CodeBox));
//}
#endregion
}
}

View File

@@ -0,0 +1,41 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
using System.Windows.Controls;
/// <summary>
/// A heading of the a page.
/// </summary>
public class Heading : TextBlock
{
#region Properties
public static readonly DependencyProperty SizeProperty =
DependencyProperty.Register("Size", typeof(HeadingSizes), typeof(Heading), new PropertyMetadata(HeadingSizes.Normal));
/// <summary>
/// Gets/sets the size of the heading.
/// </summary>
public HeadingSizes Size
{
get { return (HeadingSizes)GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
#endregion
#region Constructors
static Heading()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Heading), new FrameworkPropertyMetadata(typeof(Heading)));
}
#endregion
}
public enum HeadingSizes : byte
{
ExtraLarge, Large, Medium, Normal, Small, Tiny
}
}

View File

@@ -0,0 +1,36 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
using System.Windows.Controls.Primitives;
/// <summary>
/// A hyperlink button.
/// </summary>
public class Hyperlink : ButtonBase
{
#region Properties
public static readonly DependencyProperty UriProperty =
DependencyProperty.Register("Uri", typeof(string), typeof(Hyperlink), new PropertyMetadata(string.Empty));
/// <summary>
/// Gets or sets the uri of hyperlinks.
/// </summary>
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
#endregion
#region Constructors
static Hyperlink()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Hyperlink), new FrameworkPropertyMetadata(typeof(Hyperlink)));
}
#endregion
}
}

View File

@@ -0,0 +1,498 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using AntDesign.WPF.Contracts;
using AntDesign.WPF.Utils;
namespace AntDesign.WPF.Controls;
/// <summary>
/// 头像、警告提示语义矢量图形
/// </summary>
public class Icon : FrameworkElement, ISpinable
{
#region Fields
private Geometry definingGeometry;
#endregion
#region Document Properties
/// <summary>
/// DependencyProperty for <see cref="FontSize" /> property.
/// </summary>
public static readonly DependencyProperty FontSizeProperty =
TextElement.FontSizeProperty.AddOwner(typeof(Icon));
/// <summary>
/// The FontSize property specifies the size of the font.
/// </summary>
[TypeConverter(typeof(FontSizeConverter))]
[Localizability(LocalizationCategory.None)]
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
/// <summary>
/// DependencyProperty setter for <see cref="FontSize" /> property.
/// </summary>
/// <param name="element">The element to which to write the attached property.</param>
/// <param name="value">The property value to set</param>
public static void SetFontSize(DependencyObject element, double value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(FontSizeProperty, value);
}
/// <summary>
/// DependencyProperty getter for <see cref="FontSize" /> property.
/// </summary>
/// <param name="element">The element from which to read the attached property.</param>
[TypeConverter(typeof(FontSizeConverter))]
public static double GetFontSize(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (double)element.GetValue(FontSizeProperty);
}
/// <summary>
/// DependencyProperty for <see cref="Foreground" /> property.
/// </summary>
public static readonly DependencyProperty ForegroundProperty =
TextElement.ForegroundProperty.AddOwner(typeof(Icon));
/// <summary>
/// The Foreground property specifies the foreground brush of an element's text content.
/// </summary>
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
/// <summary>
/// DependencyProperty setter for <see cref="Foreground" /> property.
/// </summary>
/// <param name="element">The element to which to write the attached property.</param>
/// <param name="value">The property value to set</param>
public static void SetForeground(DependencyObject element, Brush value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(ForegroundProperty, value);
}
/// <summary>
/// DependencyProperty getter for <see cref="Foreground" /> property.
/// </summary>
/// <param name="element">The element from which to read the attached property.</param>
public static Brush GetForeground(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (Brush)element.GetValue(ForegroundProperty);
}
public static readonly DependencyProperty BackgroundProperty =
TextElement.BackgroundProperty.AddOwner(
typeof(Icon),
new FrameworkPropertyMetadata(
Brushes.Transparent,
FrameworkPropertyMetadataOptions.AffectsRender));
/// <summary>
/// The Background property defines the brush used to fill the content area.
/// </summary>
public Brush Background
{
get { return (Brush)GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
#endregion
#region Properties
/// <summary>
/// Get the geometry that defines this icon.
/// </summary>
protected Geometry DefiningGeometry
{
get
{
if (definingGeometry == null)
{
if (!string.IsNullOrEmpty(Type))
{
var key = "anticon." + Type.ToLower();
// With theme suffix.
if (Theme == IconTheme.Filled)
{
key += ".fill";
} else if (Theme == IconTheme.Colorful)
{
key += ".colorful";
}
definingGeometry = TryFindResource(key) as Geometry ?? Geometry.Empty;
}
else
{
definingGeometry = Geometry.Empty;
}
}
return definingGeometry;
}
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register("Type", typeof(string), typeof(Icon),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnSpinChanged));
/// <summary>
/// Gets/sets the type of the ant design icon.
/// </summary>
public string Type
{
get { return (string)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty ThemeProperty =
DependencyProperty.Register("Theme", typeof(IconTheme), typeof(Icon),
new FrameworkPropertyMetadata(IconTheme.Outlined, FrameworkPropertyMetadataOptions.AffectsRender));
/// <summary>
/// Gets/sets the theme of the ant design icon.
/// </summary>
public IconTheme Theme
{
get { return (IconTheme)GetValue(ThemeProperty); }
set { SetValue(ThemeProperty, value); }
}
public static readonly DependencyProperty SpinProperty =
DependencyProperty.Register("Spin", typeof(bool?), typeof(Icon), new PropertyMetadata(null, OnSpinChanged));
/// <summary>
/// Gets/sets whether the icon has a spin animation.
/// </summary>
public bool? Spin
{
get { return (bool?)GetValue(SpinProperty); }
set { SetValue(SpinProperty, value); }
}
private static void OnSpinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Icon).SetSpinAnimation();
}
#endregion
#region Attached Propperties
/// <summary>
/// Why is it not a dependency property?
/// icons are introduced by way of resources. if you define them as dependency properties, you will lose more flexibility.
/// For example, each icon needs to use different stretch parameters.
/// </summary>
public static readonly DependencyProperty ViewBoxProperty =
DependencyProperty.RegisterAttached("ViewBox", typeof(Rect), typeof(Icon), new PropertyMetadata(new Rect(0, 0, 1024, 1024)), OnViewBoxValidate);
private static bool OnViewBoxValidate(object value)
{
var viewBox = (Rect)value;
return viewBox.IsEmpty || (viewBox.Width >= 0 && viewBox.Height >= 0);
}
/// <summary>
/// Gets the rectangular area of the geometric stretch.
/// </summary>
[AttachedPropertyBrowsableForType(typeof(Geometry))]
public static Rect GetViewBox(DependencyObject obj)
{
return (Rect)obj.GetValue(ViewBoxProperty);
}
/// <summary>
/// Sets the rectangular area of the geometric stretch.
/// </summary>
public static void SetViewBox(DependencyObject obj, Rect value)
{
obj.SetValue(ViewBoxProperty, value);
}
/// <summary>
/// When you need colorful icons, you need to be able to support custom brushes.
/// </summary>
public static readonly DependencyProperty FillProperty =
DependencyProperty.RegisterAttached("Fill", typeof(Brush), typeof(Icon), new PropertyMetadata(null));
/// <summary>
/// Gets the brush that fill the geometry.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[AttachedPropertyBrowsableForType(typeof(Geometry))]
public static Brush GetFill(DependencyObject obj)
{
return (Brush)obj.GetValue(FillProperty);
}
/// <summary>
/// Sets the brush to fill the geometry. Valid when Theme is colorful.
/// </summary>
public static void SetFill(DependencyObject obj, Brush value)
{
obj.SetValue(FillProperty, value);
}
#endregion
#region Constructors
static Icon()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Icon), new FrameworkPropertyMetadata(typeof(Icon)));
}
public Icon()
{
Loaded += (s, e) => SetSpinAnimation();
}
#endregion
#region Overrides
/// <summary>
/// Notification that a specified property has been invalidated.
/// </summary>
/// <param name="e">EventArgs that contains the property, metadata, old value, and new value for this change</param>
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue && (e.Property == TypeProperty || e.Property == ThemeProperty))
{
// Reset definition geometry.
definingGeometry = null;
}
base.OnPropertyChanged(e);
}
/// <summary>
/// Updates DesiredSize of the icon. Called by parent UIElement during is the first pass of layout.
/// </summary>
/// <param name="constraint">Constraint size is an "upper limit" that should not exceed.</param>
/// <returns>icon desired size.</returns>
protected override Size MeasureOverride(Size constraint)
{
return GetRenderSize(constraint, FontSize);
}
/// <summary>
/// Compute the rendered geometry.
/// </summary>
/// <param name="finalSize"></param>
/// <returns></returns>
protected override Size ArrangeOverride(Size finalSize)
{
return GetRenderSize(finalSize, FontSize);
}
/// <summary>
/// Render callback.
/// </summary>
protected override void OnRender(DrawingContext dc)
{
Geometry rendered;
var geometry = DefiningGeometry;
Debug.Assert(geometry != null);
var foreground = Foreground;
var matrix = GetStretchMatrix(geometry, FontSize);
// Need to use colorful render.
if (geometry is GeometryGroup)
{
Brush brush;
int index = 0;
var isSolidColor = foreground is SolidColorBrush;
var children = ((GeometryGroup)geometry).Children;
foreach (var child in children)
{
rendered = GetRenderedGeometry(child, matrix);
if (rendered != Geometry.Empty)
{
brush = rendered.GetValue(FillProperty) as Brush;
// It may need to be tinted
if (brush == null)
{
if (!isSolidColor || index == 0 || index == 6 || index > 9)
{
brush = foreground;
}else
{
brush = new SolidColorBrush(ColorPalette.Toning(((SolidColorBrush)foreground).Color, index));
}
index++;
}
dc.DrawGeometry(brush, null, rendered);
}
}
} else
{
rendered = GetRenderedGeometry(geometry, matrix);
if (rendered != Geometry.Empty)
{
dc.DrawGeometry(foreground, null, rendered);
}
}
// Without background, the mouse can penetrate geometry and cause event failure.
var background = Background;
if (background != null)
{
dc.DrawRectangle(background, null, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
}
}
#endregion
#region Private Methods
private void SetSpinAnimation()
{
var spin = Spin ?? Type == "loading";
if (spin)
{
this.BeginSpin(1d);
}
else
{
this.StopSpin();
}
}
private Size GetRenderSize(Size availableSize, double fontSize)
{
if (IsGeometryEmpty(DefiningGeometry))
{
return new Size(0d, 0d);
}
return new Size(Math.Min(availableSize.Width, fontSize), Math.Min(availableSize.Height, fontSize));
}
private bool IsGeometryEmpty(Geometry geometry)
{
return geometry.IsEmpty() || geometry.Bounds.IsEmpty;
}
/// <summary>
/// Get the rendered geometry.
/// </summary>
private Geometry GetRenderedGeometry(Geometry geometry, Matrix matrix)
{
var rendered = geometry.CloneCurrentValue();
if (ReferenceEquals(geometry, rendered))
{
rendered = rendered.Clone();
}
var transform = rendered.Transform;
if (transform == null)
{
rendered.Transform = new MatrixTransform(matrix);
}
else
{
rendered.Transform = new MatrixTransform(transform.Value * matrix);
}
return rendered;
}
/// <summary>
/// Get the stretch matrix of the geometry.
/// </summary>
private Matrix GetStretchMatrix(Geometry geometry, double size)
{
var matrix = Matrix.Identity;
if (!IsGeometryEmpty(geometry))
{
double scaleX, scaleY;
var viewBox = (Rect)geometry.GetValue(ViewBoxProperty);
if (viewBox.IsEmpty)
{
viewBox = geometry.Bounds;
scaleX = size / viewBox.Right;
scaleY = size / viewBox.Bottom;
if (scaleX > scaleY)
{
scaleX = scaleY;
} else
{
scaleY = scaleX;
}
} else
{
scaleX = size / viewBox.Width;
scaleY = size / viewBox.Height;
matrix.Translate(-(scaleX * viewBox.X), -(scaleY * viewBox.Y));
}
matrix.Scale(scaleX, scaleY);
}
return matrix;
}
#endregion
}
public enum IconTheme : byte
{
Filled, Outlined, Colorful
}

View File

@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace AntDesign.WPF.Controls
{
/// <summary>
/// Custom window class for a Material Design like styled window.
/// </summary>
public class MaterialWindow : Window
{
private const string MinimizeButtonName = "minimizeButton";
private const string MaximizeRestoreButtonName = "maximizeRestoreButton";
private const string CloseButtonName = "closeButton";
/// <summary>
/// The color for the border and caption area background of the window.
/// </summary>
public static readonly DependencyProperty BorderBackgroundBrushProperty = DependencyProperty.Register(
nameof(BorderBackgroundBrush), typeof(Brush), typeof(MaterialWindow), new FrameworkPropertyMetadata(null, null));
/// <summary>
/// The color for the border and caption area background of the window.
/// </summary>
public Brush BorderBackgroundBrush
{
get
{
return (Brush)GetValue(BorderBackgroundBrushProperty);
}
set
{
SetValue(BorderBackgroundBrushProperty, value);
}
}
/// <summary>
/// The forground color for the caption area of the window.
/// </summary>
public static readonly DependencyProperty BorderForegroundBrushProperty = DependencyProperty.Register(
nameof(BorderForegroundBrush), typeof(Brush), typeof(MaterialWindow), new FrameworkPropertyMetadata(null, null));
/// <summary>
/// The forground color for the caption area of the window.
/// </summary>
public Brush BorderForegroundBrush
{
get
{
return (Brush)GetValue(BorderForegroundBrushProperty);
}
set
{
SetValue(BorderForegroundBrushProperty, value);
}
}
/// <summary>
/// Lets the content of the window fade out if the window is inactive.
/// The default is true (enabled).
/// </summary>
public static readonly DependencyProperty FadeContentIfInactiveProperty = DependencyProperty.Register(
nameof(FadeContentIfInactive), typeof(bool), typeof(MaterialWindow), new FrameworkPropertyMetadata(true));
/// <summary>
/// Lets the content of the window fade out if the window is inactive.
/// The default is true (enabled).
/// </summary>
public bool FadeContentIfInactive
{
get
{
return (bool)GetValue(FadeContentIfInactiveProperty);
}
set
{
SetValue(FadeContentIfInactiveProperty, value);
}
}
/// <summary>
/// The template for the title bar. The default shows a <see cref="TextBlock" /> with the title.
/// </summary>
public static readonly DependencyProperty TitleTemplateProperty = DependencyProperty.Register(
nameof(TitleTemplate), typeof(DataTemplate), typeof(MaterialWindow));
/// <summary>
/// The template for the title bar. The default shows a <see cref="TextBlock" /> with the title.
/// </summary>
public DataTemplate TitleTemplate
{
get
{
return (DataTemplate)GetValue(TitleTemplateProperty);
}
set
{
SetValue(TitleTemplateProperty, value);
}
}
private AntdButton m_minimizeButton;
private AntdButton m_maximizeRestoreButton;
private AntdButton m_closeButton;
static MaterialWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MaterialWindow), new FrameworkPropertyMetadata(typeof(MaterialWindow)));
}
/// <summary>
/// Creates a new <see cref="MaterialWindow" />.
/// </summary>
public MaterialWindow() : base() { }
public override void OnApplyTemplate()
{
if (m_minimizeButton != null)
{
m_minimizeButton.Click -= MinimizeButtonClickHandler;
}
m_minimizeButton = GetTemplateChild(MinimizeButtonName) as AntdButton;
if (m_minimizeButton != null)
{
m_minimizeButton.Click += MinimizeButtonClickHandler;
}
if (m_maximizeRestoreButton != null)
{
m_maximizeRestoreButton.Click -= MaximizeRestoreButtonClickHandler;
}
m_maximizeRestoreButton = GetTemplateChild(MaximizeRestoreButtonName) as AntdButton;
if (m_maximizeRestoreButton != null)
{
m_maximizeRestoreButton.Click += MaximizeRestoreButtonClickHandler;
}
if (m_closeButton != null)
{
m_closeButton.Click -= CloseButtonClickHandler;
}
m_closeButton = GetTemplateChild(CloseButtonName) as AntdButton;
if (m_closeButton != null)
{
m_closeButton.Click += CloseButtonClickHandler;
}
base.OnApplyTemplate();
}
private void CloseButtonClickHandler(object sender, RoutedEventArgs args)
{
Close();
}
private void MaximizeRestoreButtonClickHandler(object sender, RoutedEventArgs args)
{
WindowState = WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal;
}
private void MinimizeButtonClickHandler(object sender, RoutedEventArgs args)
{
WindowState = WindowState.Minimized;
}
}
}

View File

@@ -0,0 +1,111 @@
namespace AntDesign.WPF.Controls
{
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
/// <summary>
/// AddValueChanged of dependency property descriptor results in memory leak as you already know.
/// So, as described here, you can create custom class PropertyChangeNotifier to listen
/// to any dependency property changes.
///
/// This class takes advantage of the fact that bindings use weak references to manage associations
/// so the class will not root the object who property changes it is watching. It also uses a WeakReference
/// to maintain a reference to the object whose property it is watching without rooting that object.
/// In this way, you can maintain a collection of these objects so that you can unhook the property
/// change later without worrying about that collection rooting the object whose values you are watching.
///
/// Complete implementation can be found here: http://agsmith.wordpress.com/2008/04/07/propertydescriptor-addvaluechanged-alternative/
/// </summary>
internal sealed class PropertyChangeNotifier : DependencyObject, IDisposable
{
private WeakReference _propertySource;
public PropertyChangeNotifier(DependencyObject propertySource, string path)
: this(propertySource, new PropertyPath(path))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, DependencyProperty property)
: this(propertySource, new PropertyPath(property))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, PropertyPath property)
{
if (null == propertySource)
{
throw new ArgumentNullException(nameof(propertySource));
}
if (null == property)
{
throw new ArgumentNullException(nameof(property));
}
this._propertySource = new WeakReference(propertySource);
var binding = new Binding();
binding.Path = property;
binding.Mode = BindingMode.OneWay;
binding.Source = propertySource;
BindingOperations.SetBinding(this, ValueProperty, binding);
}
public DependencyObject PropertySource
{
get
{
try
{
// note, it is possible that accessing the target property
// will result in an exception so ive wrapped this check
// in a try catch
return this._propertySource.IsAlive
? this._propertySource.Target as DependencyObject
: null;
}
catch
{
return null;
}
}
}
/// <summary>
/// Identifies the <see cref="Value"/> dependency property
/// </summary>
public static readonly DependencyProperty ValueProperty
= DependencyProperty.Register("Value", typeof(object), typeof(PropertyChangeNotifier),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnPropertyChanged)));
/// <summary>
/// Returns/sets the value of the property
/// </summary>
/// <seealso cref="ValueProperty"/>
[Description("Returns/sets the value of the property")]
[Category("Behavior")]
[Bindable(true)]
public object Value
{
get { return (object)this.GetValue(ValueProperty); }
set { this.SetValue(ValueProperty, value); }
}
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var notifier = (PropertyChangeNotifier)d;
if (notifier.RaiseValueChanged)
{
notifier.ValueChanged?.Invoke(notifier.PropertySource, EventArgs.Empty);
}
}
public event EventHandler ValueChanged;
public bool RaiseValueChanged { get; set; } = true;
public void Dispose()
{
BindingOperations.ClearBinding(this, ValueProperty);
}
}
}

View File

@@ -0,0 +1,61 @@
namespace AntDesign.WPF.Controls
{
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Windows;
using System.Windows.Controls;
public class RadioButtonGroup : ItemsControl
{
#region Properties
public static readonly DependencyProperty SizeProperty =
DependencyProperty.Register("Size", typeof(Sizes?), typeof(RadioButtonGroup), new PropertyMetadata(null));
/// <summary>
/// Gets/sets size for radio button style
/// </summary>
public Sizes? Size
{
get { return (Sizes?)GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(RadioButtonGroup), new PropertyMetadata(Orientation.Horizontal));
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
#endregion
#region Constructors
static RadioButtonGroup()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(RadioButtonGroup), new FrameworkPropertyMetadata(typeof(RadioButtonGroup)));
}
#endregion
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
}
}
public class RadioButtonGroupTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return base.SelectTemplate(item, container);
}
}
}

View File

@@ -0,0 +1,7 @@
namespace AntDesign.WPF.Controls
{
public enum Shapes : byte
{
Circle, Square
}
}

View File

@@ -0,0 +1,7 @@
namespace AntDesign.WPF.Controls
{
public enum Sizes
{
Large = -1, Small = -2
}
}

View File

@@ -0,0 +1,94 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
using ContentControlBase = System.Windows.Controls.ContentControl;
/// <summary>
/// A spinner for displaying loading state of a page or a section.
/// </summary>
[TemplateVisualState(Name = "Spun", GroupName = "SpinStates")]
[TemplateVisualState(Name = "Unspun", GroupName = "SpinStates")]
public class Spin : ContentControlBase
{
#region Properties
public static readonly DependencyProperty IndicatorProperty =
DependencyProperty.Register("Indicator", typeof(UIElement), typeof(Spin), new PropertyMetadata(null, OnSpinningChanged));
/// <summary>
/// Gets/sets element of the spinning indicator.
/// </summary>
public UIElement Indicator
{
get { return (UIElement)GetValue(IndicatorProperty); }
set { SetValue(IndicatorProperty, value); }
}
public static readonly DependencyProperty SizeProperty =
DependencyProperty.Register("Size", typeof(Sizes?), typeof(Spin), new PropertyMetadata(null));
/// <summary>
/// Gets/sets size of spin.
/// </summary>
public Sizes? Size
{
get { return (Sizes?)GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
public static readonly DependencyProperty SpinningProperty =
DependencyProperty.Register("Spinning", typeof(bool), typeof(Spin), new PropertyMetadata(true, OnSpinningChanged));
/// <summary>
/// Gets/sets whether spin is spinning.
/// </summary>
public bool Spinning
{
get { return (bool)GetValue(SpinningProperty); }
set { SetValue(SpinningProperty, value); }
}
private static void OnSpinningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Spin).GoToSpinState();
}
private void GoToSpinState()
{
VisualStateManager.GoToState(this, Spinning && null == Indicator ? "Spun" : "Unspun", true);
}
public static readonly DependencyProperty TipProperty =
DependencyProperty.Register("Tip", typeof(string), typeof(Spin), new PropertyMetadata(string.Empty));
/// <summary>
/// Gets/sets customize description content when spin has children.
/// </summary>
public string Tip
{
get { return (string)GetValue(TipProperty); }
set { SetValue(TipProperty, value); }
}
#endregion
#region Constructors
static Spin()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Spin), new FrameworkPropertyMetadata(typeof(Spin)));
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
GoToSpinState();
}
#endregion
}
}

View File

@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Animation;
namespace AntDesign.WPF.Controls
{
[TemplatePart(Name = PART_Dot, Type = typeof(FrameworkElement))]
public class Switch : ToggleButton
{
#region Fields
private const string PART_Dot = "PART_Dot";
private FrameworkElement dot;
private VisualState pressedState;
private Storyboard pressedStoryboard;
#endregion
#region Properties
public static readonly DependencyProperty LoadingProperty =
DependencyProperty.Register("Loading", typeof(bool), typeof(Switch), new PropertyMetadata(false));
/// <summary>
/// Gets/sets loading state of switch.
/// </summary>
public bool Loading
{
get { return (bool)GetValue(LoadingProperty); }
set { SetValue(LoadingProperty, value); }
}
public static readonly DependencyProperty SizeProperty =
DependencyProperty.Register("Size", typeof(Sizes?), typeof(Switch), new PropertyMetadata(null));
/// <summary>
/// Gets/sets the size of the switch.
/// </summary>
public Sizes? Size
{
get { return (Sizes?)GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
public static readonly DependencyProperty UnCheckedContentProperty =
DependencyProperty.Register("UnCheckedContent", typeof(object), typeof(Switch), new PropertyMetadata(null));
/// <summary>
/// Gets/sets content to be shown when the state is unchecked.
/// </summary>
public object UnCheckedContent
{
get { return GetValue(UnCheckedContentProperty); }
set { SetValue(UnCheckedContentProperty, value); }
}
#endregion
#region Constructors
static Switch()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Switch), new FrameworkPropertyMetadata(typeof(Switch)));
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
dot = GetTemplateChild(PART_Dot) as FrameworkElement;
pressedState = GetTemplateChild("Pressed") as VisualState;
if (pressedState != null && pressedState.Storyboard != null)
{
pressedStoryboard = pressedState.Storyboard.Clone();
}
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
SetDotAnimation();
}
private bool DotAnimated = false;
protected override void OnIsPressedChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsPressedChanged(e);
//if (!DotAnimated)
//{
if (dot != null /*&& IsChecked.HasValue && IsChecked.Value*/ && IsPressed)
{
var to = dot.Margin;
// to.Left -= dot.ActualWidth * 1.3333 - dot.ActualWidth;
to.Right -= dot.ActualWidth * 1.3333 - dot.ActualWidth;
var ease = new CircleEase() { EasingMode = EasingMode.EaseInOut };
var animation = new ThicknessAnimation(to, TimeSpan.FromSeconds(0.36)) { EasingFunction = ease };
dot.BeginAnimation(MarginProperty, animation);
}
//else if (!IsPressed) DotAnimated = false;
//}
}
private void SetDotAnimation()
{
if (dot == null || pressedState == null || DotAnimated) return;
DotAnimated = true;
var storyboard = pressedStoryboard != null ? pressedStoryboard.Clone() : new Storyboard();
var ease = new CircleEase() { EasingMode = EasingMode.EaseInOut };
var animation = new DoubleAnimation(dot.ActualWidth * 1.3333, TimeSpan.FromSeconds(0.36)) { EasingFunction = ease };
Storyboard.SetTargetName(animation, PART_Dot);
Storyboard.SetTargetProperty(animation, new PropertyPath("Width"));
storyboard.Children.Add(animation);
pressedState.Storyboard = storyboard;
}
#endregion
}
}

View File

@@ -0,0 +1,16 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
public class TabControl : System.Windows.Controls.TabControl
{
#region Constructors
static TabControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TabControl), new FrameworkPropertyMetadata(typeof(TabControl)));
}
#endregion
}
}

View File

@@ -0,0 +1,144 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
using ContentControlBase = System.Windows.Controls.ContentControl;
/// <summary>
/// Tag for categorizing or markup.
/// </summary>
[TemplatePart(Name = PART_Close, Type = typeof(UIElement))]
public class Tag : ContentControlBase
{
#region Fileds
private const string PART_Close = "PART_Close";
private UIElement close;
#endregion
#region Events
public static readonly RoutedEvent ClosingEvent =
EventManager.RegisterRoutedEvent("Closing", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Tag));
/// <summary>
/// Occurs when closing the tag.
/// </summary>
public event RoutedEventHandler Closing
{
add { AddHandler(ClosingEvent, value); }
remove { RemoveHandler(ClosingEvent, value); }
}
public static readonly RoutedEvent ClosedEvent =
EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Tag));
/// <summary>
/// Occurs when a Tag is closed and is no longer visible.
/// </summary>
public event RoutedEventHandler Closed
{
add { AddHandler(ClosedEvent, value); }
remove { RemoveHandler(ClosedEvent, value); }
}
#endregion
#region Properties
public static readonly DependencyProperty ClosableProperty =
DependencyProperty.Register("Closable", typeof(bool), typeof(Tag), new PropertyMetadata(false, OnClosableChnaged));
/// <summary>
/// Gets/sets whether the Tag can be closed.
/// </summary>
public bool Closable
{
get { return (bool)GetValue(ClosableProperty); }
set { SetValue(ClosableProperty, value); }
}
private static void OnClosableChnaged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Tag).SetCloseVisibility();
}
private void SetCloseVisibility()
{
if (close != null)
{
close.Visibility = Closable ? Visibility.Visible : Visibility.Collapsed;
}
}
public static readonly DependencyProperty ColorModeProperty =
DependencyProperty.Register("ColorMode", typeof(ColorMode?), typeof(Tag), new PropertyMetadata(null));
/// <summary>
/// Gets/sets the color mode of the tag.
/// </summary>
public ColorMode? ColorMode
{
get { return (ColorMode?)GetValue(ColorModeProperty); }
set { SetValue(ColorModeProperty, value); }
}
#endregion
#region Constructors
static Tag()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Tag), new FrameworkPropertyMetadata(typeof(Tag)));
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
close = GetTemplateChild(PART_Close) as UIElement;
if (close != null)
{
Loaded -= OnLoaded;
Loaded += OnLoaded;
close.MouseLeftButtonUp -= OnRaiseClosingEvent;
close.MouseLeftButtonUp += OnRaiseClosingEvent;
SetCloseVisibility();
}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Closing -= OnClosing;
Closing += OnClosing;
}
private void OnRaiseClosingEvent(object sender, RoutedEventArgs e)
{
e.Handled = true;
RaiseEvent(new RoutedEventArgs(ClosingEvent, this));
}
private void OnClosing(object sender, RoutedEventArgs e)
{
SetCurrentValue(VisibilityProperty, Visibility.Collapsed);
RaiseEvent(new RoutedEventArgs(ClosedEvent, this));
}
#endregion
}
public enum ColorMode : byte
{
Colorful, Inverse
}
}

View File

@@ -0,0 +1,58 @@
using AntDesign.WPF.Contracts;
namespace AntDesign.WPF.Controls
{
using System.Windows.Input;
using WPF.Contracts;
using ThumbBase = System.Windows.Controls.Primitives.Thumb;
public class Thumb : ThumbBase, IThumb
{
private TouchDevice currentDevice = null;
protected override void OnPreviewTouchDown(TouchEventArgs e)
{
// Release any previous capture
this.ReleaseCurrentDevice();
// Capture the new touch
this.CaptureCurrentDevice(e);
}
protected override void OnPreviewTouchUp(TouchEventArgs e)
{
this.ReleaseCurrentDevice();
}
protected override void OnLostTouchCapture(TouchEventArgs e)
{
// Only re-capture if the reference is not null
// This way we avoid re-capturing after calling ReleaseCurrentDevice()
if (this.currentDevice != null)
{
this.CaptureCurrentDevice(e);
}
}
private void ReleaseCurrentDevice()
{
if (this.currentDevice != null)
{
// Set the reference to null so that we don't re-capture in the OnLostTouchCapture() method
var temp = this.currentDevice;
this.currentDevice = null;
this.ReleaseTouchCapture(temp);
}
}
private void CaptureCurrentDevice(TouchEventArgs e)
{
bool gotTouch = this.CaptureTouch(e.TouchDevice);
if (gotTouch)
{
this.currentDevice = e.TouchDevice;
}
}
}
}

View File

@@ -0,0 +1,263 @@
using AntDesign.WPF.Contracts;
namespace AntDesign.WPF.Controls
{
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using WPF.Contracts;
public class ThumbContentControl : AntdContentControl, IThumb
{
#region Events
public static readonly RoutedEvent DragStartedEvent
= EventManager.RegisterRoutedEvent("DragStarted", RoutingStrategy.Bubble, typeof(DragStartedEventHandler), typeof(ThumbContentControl));
/// <summary>
/// Adds or remove a DragStartedEvent handler
/// </summary>
public event DragStartedEventHandler DragStarted
{
add { AddHandler(DragStartedEvent, value); }
remove { RemoveHandler(DragStartedEvent, value); }
}
public static readonly RoutedEvent DragDeltaEvent
= EventManager.RegisterRoutedEvent("DragDelta", RoutingStrategy.Bubble, typeof(DragDeltaEventHandler), typeof(ThumbContentControl));
/// <summary>
/// Adds or remove a DragDeltaEvent handler
/// </summary>
public event DragDeltaEventHandler DragDelta
{
add { AddHandler(DragDeltaEvent, value); }
remove { RemoveHandler(DragDeltaEvent, value); }
}
public static readonly RoutedEvent DragCompletedEvent
= EventManager.RegisterRoutedEvent("DragCompleted", RoutingStrategy.Bubble, typeof(DragCompletedEventHandler), typeof(ThumbContentControl));
/// <summary>
/// Adds or remove a DragCompletedEvent handler
/// </summary>
public event DragCompletedEventHandler DragCompleted
{
add { AddHandler(DragCompletedEvent, value); }
remove { RemoveHandler(DragCompletedEvent, value); }
}
#endregion
#region Fields
private TouchDevice currentDevice = null;
private Point startDragPoint;
private Point startDragScreenPoint;
private Point oldDragScreenPoint;
#endregion
#region Properties
public static readonly DependencyPropertyKey IsDraggingPropertyKey
= DependencyProperty.RegisterReadOnly("IsDragging", typeof(bool), typeof(ThumbContentControl), new FrameworkPropertyMetadata(default(bool)));
/// <summary>
/// DependencyProperty for the IsDragging property.
/// </summary>
public static readonly DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty;
/// <summary>
/// Indicates that the left mouse button is pressed and is over the MetroThumbContentControl.
/// </summary>
public bool IsDragging
{
get { return (bool)GetValue(IsDraggingProperty); }
protected set { SetValue(IsDraggingPropertyKey, value); }
}
#endregion
#region Constructors
static ThumbContentControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ThumbContentControl), new FrameworkPropertyMetadata(typeof(ThumbContentControl)));
FocusableProperty.OverrideMetadata(typeof(ThumbContentControl), new FrameworkPropertyMetadata(default(bool)));
EventManager.RegisterClassHandler(typeof(ThumbContentControl), Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture));
}
#endregion
#region Overrides
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (!IsDragging)
{
e.Handled = true;
try
{
// focus me
Focus();
// now capture the mouse for the drag action
CaptureMouse();
// so now we are in dragging mode
SetValue(IsDraggingPropertyKey, true);
// get the mouse points
startDragPoint = e.GetPosition(this);
oldDragScreenPoint = startDragScreenPoint = PointToScreen(startDragPoint);
RaiseEvent(new ThumbContentControlDragStartedEventArgs(startDragPoint.X, startDragPoint.Y));
}
catch (Exception exception)
{
Trace.TraceError($"{this}: Something went wrong here: {exception} {Environment.NewLine} {exception.StackTrace}");
CancelDragAction();
}
}
base.OnMouseLeftButtonDown(e);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (IsMouseCaptured && IsDragging)
{
e.Handled = true;
// now we are in normal mode
ClearValue(IsDraggingPropertyKey);
// release the captured mouse
ReleaseMouseCapture();
// get the current mouse position and call the completed event with the horizontal/vertical change
Point currentMouseScreenPoint = PointToScreen(e.MouseDevice.GetPosition(this));
var horizontalChange = currentMouseScreenPoint.X - startDragScreenPoint.X;
var verticalChange = currentMouseScreenPoint.Y - startDragScreenPoint.Y;
RaiseEvent(new ThumbContentControlDragCompletedEventArgs(horizontalChange, verticalChange, false));
}
base.OnMouseLeftButtonUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (!IsDragging) return;
if (e.MouseDevice.LeftButton == MouseButtonState.Pressed)
{
Point currentDragPoint = e.GetPosition(this);
// Get client point and convert it to screen point
Point currentDragScreenPoint = PointToScreen(currentDragPoint);
if (currentDragScreenPoint != oldDragScreenPoint)
{
oldDragScreenPoint = currentDragScreenPoint;
e.Handled = true;
var horizontalChange = currentDragPoint.X - startDragPoint.X;
var verticalChange = currentDragPoint.Y - startDragPoint.Y;
RaiseEvent(new DragDeltaEventArgs(horizontalChange, verticalChange) { RoutedEvent = ThumbContentControl.DragDeltaEvent });
}
}
else
{
// clear some saved stuff
if (e.MouseDevice.Captured == this)
{
ReleaseMouseCapture();
}
ClearValue(IsDraggingPropertyKey);
startDragPoint.X = 0;
startDragPoint.Y = 0;
}
}
protected override void OnPreviewTouchDown(TouchEventArgs e)
{
// Release any previous capture
ReleaseCurrentDevice();
// Capture the new touch
CaptureCurrentDevice(e);
}
protected override void OnPreviewTouchUp(TouchEventArgs e)
{
ReleaseCurrentDevice();
}
protected override void OnLostTouchCapture(TouchEventArgs e)
{
// Only re-capture if the reference is not null
// This way we avoid re-capturing after calling ReleaseCurrentDevice()
if (currentDevice != null)
{
CaptureCurrentDevice(e);
}
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ThumbContentControlAutomationPeer(this);
}
#endregion
#region Methods
private static void OnLostMouseCapture(object sender, MouseEventArgs e)
{
// Cancel the drag action if we lost capture
var thumb = (ThumbContentControl)sender;
if (Mouse.Captured != thumb)
{
thumb.CancelDragAction();
}
}
private void ReleaseCurrentDevice()
{
if (currentDevice != null)
{
// Set the reference to null so that we don't re-capture in the OnLostTouchCapture() method
var temp = currentDevice;
currentDevice = null;
ReleaseTouchCapture(temp);
}
}
private void CaptureCurrentDevice(TouchEventArgs e)
{
bool gotTouch = CaptureTouch(e.TouchDevice);
if (gotTouch)
{
currentDevice = e.TouchDevice;
}
}
public void CancelDragAction()
{
if (!IsDragging) return;
if (IsMouseCaptured)
{
ReleaseMouseCapture();
}
ClearValue(IsDraggingPropertyKey);
var horizontalChange = oldDragScreenPoint.X - startDragScreenPoint.X;
var verticalChange = oldDragScreenPoint.Y - startDragScreenPoint.Y;
RaiseEvent(new ThumbContentControlDragCompletedEventArgs(horizontalChange, verticalChange, true));
}
#endregion
}
}

View File

@@ -0,0 +1,23 @@
namespace AntDesign.WPF.Controls
{
using System.Windows;
using System.Windows.Automation.Peers;
public class ThumbContentControlAutomationPeer : FrameworkElementAutomationPeer
{
public ThumbContentControlAutomationPeer(FrameworkElement owner)
: base(owner)
{
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
protected override string GetClassNameCore()
{
return "ThumbContentControl";
}
}
}

View File

@@ -0,0 +1,13 @@
namespace AntDesign.WPF.Controls
{
using System.Windows.Controls.Primitives;
public class ThumbContentControlDragCompletedEventArgs : DragCompletedEventArgs
{
public ThumbContentControlDragCompletedEventArgs(double horizontalOffset, double verticalOffset, bool canceled)
: base(horizontalOffset, verticalOffset, canceled)
{
this.RoutedEvent = ThumbContentControl.DragCompletedEvent;
}
}
}

View File

@@ -0,0 +1,13 @@
namespace AntDesign.WPF.Controls
{
using System.Windows.Controls.Primitives;
public class ThumbContentControlDragStartedEventArgs : DragStartedEventArgs
{
public ThumbContentControlDragStartedEventArgs(double horizontalOffset, double verticalOffset)
: base(horizontalOffset, verticalOffset)
{
this.RoutedEvent = ThumbContentControl.DragStartedEvent;
}
}
}

View File

@@ -0,0 +1,296 @@
using System.ComponentModel;
using System.Text;
using System.Windows.Threading;
using System.Windows.Controls.Primitives;
using AntDesign.WPF.Win32;
using SystemCommands = System.Windows.SystemCommands;
namespace AntDesign.WPF.Controls
{
[TemplatePart(Name = PART_Min, Type = typeof(ButtonBase))]
[TemplatePart(Name = PART_Max, Type = typeof(ButtonBase))]
[TemplatePart(Name = PART_Close, Type = typeof(ButtonBase))]
public class WindowButtons : ContentControl, INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event ClosingWindowEventHandler ClosingWindow;
public delegate void ClosingWindowEventHandler(object sender, ClosingWindowEventHandlerArgs args);
#endregion
#region Fields
private const string PART_Min = "PART_Min";
private const string PART_Max = "PART_Max";
private const string PART_Close = "PART_Close";
private ButtonBase min;
private ButtonBase max;
private ButtonBase close;
private SafeLibraryHandle user32;
#endregion
#region Properties
public static readonly DependencyProperty MinimizeProperty =
DependencyProperty.Register("Minimize", typeof(string), typeof(WindowButtons), new PropertyMetadata(null));
/// <summary>
/// Gets or sets the minimize button tooltip.
/// </summary>
public string Minimize
{
get { return (string)GetValue(MinimizeProperty); }
set { SetValue(MinimizeProperty, value); }
}
public static readonly DependencyProperty MaximizeProperty =
DependencyProperty.Register("Maximize", typeof(string), typeof(WindowButtons), new PropertyMetadata(null));
/// <summary>
/// Gets or sets the maximize button tooltip.
/// </summary>
public string Maximize
{
get { return (string)GetValue(MaximizeProperty); }
set { SetValue(MaximizeProperty, value); }
}
public static readonly DependencyProperty RestoreProperty =
DependencyProperty.Register("Restore", typeof(string), typeof(WindowButtons), new PropertyMetadata(null));
/// <summary>
/// Gets or sets the restore button tooltip.
/// </summary>
public string Restore
{
get { return (string)GetValue(RestoreProperty); }
set { SetValue(RestoreProperty, value); }
}
public static readonly DependencyProperty CloseProperty =
DependencyProperty.Register("Close", typeof(string), typeof(WindowButtons), new PropertyMetadata(null));
/// <summary>
/// Gets or sets the close button tooltip.
/// </summary>
public string Close
{
get { return (string)GetValue(CloseProperty); }
set { SetValue(CloseProperty, value); }
}
public static readonly DependencyProperty MinimizeStyleProperty =
DependencyProperty.Register("MinimizeStyle", typeof(Style), typeof(WindowButtons), new PropertyMetadata(null, OnStyleChanged));
/// <summary>
/// Gets or sets the style for the minimize button.
/// </summary>
public Style MinimizeStyle
{
get { return (Style)GetValue(MinimizeStyleProperty); }
set { SetValue(MinimizeStyleProperty, value); }
}
public static readonly DependencyProperty MaximizeStyleProperty =
DependencyProperty.Register("MaximizeStyle", typeof(Style), typeof(WindowButtons), new PropertyMetadata(null, OnStyleChanged));
/// <summary>
/// Gets or sets the style for the maximize button.
/// </summary>
public Style MaximizeStyle
{
get { return (Style)GetValue(MaximizeStyleProperty); }
set { SetValue(MaximizeStyleProperty, value); }
}
public static readonly DependencyProperty CloseStyleProperty =
DependencyProperty.Register("CloseStyle", typeof(Style), typeof(WindowButtons), new PropertyMetadata(null, OnStyleChanged));
/// <summary>
/// Gets or sets the style for the close button.
/// </summary>
public Style CloseStyle
{
get { return (Style)GetValue(CloseStyleProperty); }
set { SetValue(CloseStyleProperty, value); }
}
private static void OnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == e.OldValue) return;
(d as WindowButtons).ApplyStyle();
}
private AntdWindow parentWindow;
public AntdWindow ParentWindow
{
get { return parentWindow; }
set
{
if (Equals(parentWindow, value))
{
return;
}
parentWindow = value;
RaisePropertyChanged("ParentWindow");
}
}
#endregion
#region Constructors
static WindowButtons()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowButtons), new FrameworkPropertyMetadata(typeof(WindowButtons)));
}
public WindowButtons()
{
Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
new Action(() =>
{
if (string.IsNullOrWhiteSpace(Minimize))
{
SetCurrentValue(MinimizeProperty, GetCaption(900));
}
if (string.IsNullOrWhiteSpace(Maximize))
{
SetCurrentValue(MaximizeProperty, GetCaption(901));
}
if (string.IsNullOrWhiteSpace(Close))
{
SetCurrentValue(CloseProperty, GetCaption(905));
}
if (string.IsNullOrWhiteSpace(Restore))
{
SetCurrentValue(RestoreProperty, GetCaption(903));
}
}));
}
private string GetCaption(int id)
{
if (user32 == null)
{
user32 = UnsafeNativeMethods.LoadLibrary(Environment.SystemDirectory + "\\User32.dll");
}
var sb = new StringBuilder(256);
UnsafeNativeMethods.LoadString(user32, (uint)id, sb, sb.Capacity);
return sb.ToString().Replace("&", "");
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
close = Template.FindName(PART_Close, this) as ButtonBase;
if (close != null)
{
close.Click += OnClose;
}
max = Template.FindName(PART_Max, this) as ButtonBase;
if (max != null)
{
max.Click += OnMaximize;
}
min = Template.FindName(PART_Min, this) as ButtonBase;
if (min != null)
{
min.Click += OnMinimize;
}
ApplyStyle();
}
#endregion
#region Methods
private void ApplyStyle()
{
if (min != null)
{
min.Style = MinimizeStyle;
}
if (max != null)
{
max.Style = MaximizeStyle;
}
if (close != null)
{
close.Style = CloseStyle;
}
}
#pragma warning disable 618
private void OnMinimize(object sender, RoutedEventArgs e)
{
if (ParentWindow != null)
{
SystemCommands.MinimizeWindow(ParentWindow);
}
}
private void OnMaximize(object sender, RoutedEventArgs e)
{
if (null == ParentWindow) return;
if (ParentWindow.WindowState == WindowState.Maximized)
{
SystemCommands.RestoreWindow(ParentWindow);
}
else
{
SystemCommands.MaximizeWindow(ParentWindow);
}
}
private void OnClose(object sender, RoutedEventArgs e)
{
var closingWindowEventHandlerArgs = new ClosingWindowEventHandlerArgs();
OnClosingWindow(closingWindowEventHandlerArgs);
if (closingWindowEventHandlerArgs.Cancelled) return;
ParentWindow?.Close();
}
protected void OnClosingWindow(ClosingWindowEventHandlerArgs args)
{
ClosingWindow?.Invoke(this, args);
}
#endregion
}
}

View File

@@ -0,0 +1,71 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace AntDesign.WPF.Controls
{
public class WindowCommands : ItemsControl, INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Properties
private AntdWindow _parentWindow;
public AntdWindow ParentWindow
{
get { return _parentWindow; }
set
{
if (Equals(_parentWindow, value))
{
return;
}
_parentWindow = value;
RaisePropertyChanged("ParentWindow");
}
}
public static readonly DependencyProperty SeparatorHeightProperty =
DependencyProperty.Register("SeparatorHeight", typeof(double), typeof(WindowCommands), new PropertyMetadata(double.NaN));
/// <summary>
/// Gets/sets the height of the separator.
/// </summary>
public double SeparatorHeight
{
get { return (double)GetValue(SeparatorHeightProperty); }
set { SetValue(SeparatorHeightProperty, value); }
}
#endregion
#region Constructors
static WindowCommands()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowCommands), new FrameworkPropertyMetadata(typeof(WindowCommands)));
}
#endregion
}
public class WindowCommandsItem : AntdContentControl
{
static WindowCommandsItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowCommandsItem), new FrameworkPropertyMetadata(typeof(WindowCommandsItem)));
}
}
}