WPF(自适应圆)
在WPF中可以实现椭圆,利用Ellipse或者Border。但是要实现圆,就必须设定固定的长宽,然后根据长宽设置对应的倒角才可以实现圆。如果这样设计,就固定了长宽,不能根据界面自适应调整。所以我们可以根据绑定和转换器将实际的长宽转换为对应的倒角。
using System;
using System.Globalization;
using System.Windows.Data;
[ValueConversion(typeof(double), typeof(double[]))]
public class RoundMathConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
{
throw new NotImplementedException();
}
double.TryParse(values[0].ToString(), out double d1);
double.TryParse(values[1].ToString(), out double d2);
if (d1 * d2 == 0)
{
return double.NaN;
}
return Math.Min(d1, d2);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
[ValueConversion(typeof(System.Windows.CornerRadius), typeof(double[]))]
public class RoundRadiusConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
{
throw new NotImplementedException();
}
double.TryParse(values[0].ToString(), out double d1);
double.TryParse(values[1].ToString(), out double d2);
double ratio = 1;
if (values.Length >= 3)
{
double.TryParse(values[2].ToString(), out ratio);
}
return new System.Windows.CornerRadius(Math.Min(d1, d2) * ratio / 2);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
1.椭圆
using System.Windows;
using System.Windows.Controls;
/// <summary>
/// EllipseBorder.xaml 的交互逻辑
/// </summary>
public partial class EllipseBorder : Border
{
public static readonly DependencyProperty RadiusRatioProperty =
DependencyProperty.Register(nameof(RadiusRatio), typeof(float), typeof(EllipseBorder), new FrameworkPropertyMetadata(1f));
public float RadiusRatio
{
get { return (float)GetValue(RadiusRatioProperty); }
set { SetValue(RadiusRatioProperty, value); }
}
public EllipseBorder()
{
InitializeComponent();
}
}
<Border x:Class="WpfApp360.CustomControl.EllipseBorder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp360.Converter"
xmlns:con="clr-namespace:WpfApp360.Converter"
d:DesignHeight="450" d:DesignWidth="800" mc:Ignorable="d">
<Border.CornerRadius>
<MultiBinding>
<MultiBinding.Converter>
<con:RoundRadiusConverter />
</MultiBinding.Converter>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}" />
<Binding Path="RadiusRatio" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Border.CornerRadius>
</Border>
可通过设置RadiusRatio来控制倒角的比例。
2.圆
由于圆需要根据ActualHeight和ActualWidth 来设置元素的Width和Height,但是这两者之间会互相影响,所以我们不能和椭圆一样来定义一个Border。下面我们设计一个圆形Button,在Button模板中定义一个Border。
<Button x:Class="WpfApp360.CustomControl.RoundButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:con="clr-namespace:WpfApp360.Converter"
d:DesignHeight="400" d:DesignWidth="400" mc:Ignorable="d">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Border.CornerRadius>
<MultiBinding>
<MultiBinding.Converter>
<con:RoundRadiusConverter />
</MultiBinding.Converter>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="ActualHeight" RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding>
</Border.CornerRadius>
<Border.Width>
<MultiBinding>
<MultiBinding.Converter>
<con:RoundMathConverter />
</MultiBinding.Converter>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="ActualHeight" RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding>
</Border.Width>
<Border.Height>
<MultiBinding>
<MultiBinding.Converter>
<con:RoundMathConverter />
</MultiBinding.Converter>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="ActualHeight" RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding>
</Border.Height>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>