如何使用MVVM根据画布大小将形状居中

时间:2019-01-11 03:26:30

标签: c# wpf xaml canvas mvvm

有一个代码来显示一个圆(使用宽度和高度相等的ellipse,并且还使用reactive ui进行通知)我想在画布中间绘制一个圆,但是管理调整大小的更新。

当前代码设置了Canvas LeftCanvas Top,但是我不确定如何在中间设置圆圈并填充几乎所有画布。

课程:

public class MyViewModel : ReactiveObject
{
    public ObservableCollection<IShape> Shapes
    {
        get => _shapes;
        set => this.RaiseAndSetIfChanged(ref _shapes, value);
    }
    private ObservableCollection<IShape> _shapes;

    public MainViewModel()
    {
      //here the location is set, but how to adjust it to the center of canvas?
        Shapes = new ObservableCollection<IShape>
        {
            new Circle {
                Top = 100,
                Left = 100,
                Radius = 50,
                Color = Color.FromArgb(255, 233,222, 0) 
            }                
        };
    }       
}

public interface IShape
{
    int Top { get; set; }
    int Left { get; set; }
}

public abstract class Shape : ReactiveObject, IShape
{
    private int _top;
    private int _left;

    public int Top
    {
        get { return _top; }
        set { this.RaiseAndSetIfChanged(ref _top, value); }
    }

    public int Left
    {
        get { return _left; }
        set { this.RaiseAndSetIfChanged(ref _left, value); }
    }
}

public class Circle : Shape
{
    private int _radius;
    private Color _color;

    public int Radius
    {
        get => _radius;
        set => this.RaiseAndSetIfChanged(ref _radius, value);
    }

    public System.Windows.Media.Color Color
    {
        get => _color;
        set => this.RaiseAndSetIfChanged(ref _color, value);
    }
}

xaml:

<ItemsControl ItemsSource="{Binding Path=Shapes}">
        <ItemsControl.Resources>           
            <DataTemplate DataType="{x:Type entities:Circle}">
                <Ellipse Width="{Binding Radius}" 
                         Height="{Binding Radius}"
                         Canvas.Top="{Binding Top, Mode=TwoWay}" 
                         Canvas.Left="{Binding Left, Mode=TwoWay}"
                         >
                    <Ellipse.Stroke>
                        <SolidColorBrush Color="{Binding Color}" />
                    </Ellipse.Stroke>
                </Ellipse>
            </DataTemplate>

        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Top" Value="{Binding Path=Top, Mode=TwoWay}" />
                <Setter Property="Canvas.Left" Value="{Binding Path=Left, Mode=TwoWay}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>

这将产生:

enter image description here

如何更改 xaml 后面的代码以居中并将圆几乎设置为画布大小?

enter image description here

3 个答案:

答案 0 :(得分:1)

通过将椭圆大小绑定到画布大小并使用将输入值转换为(例如)画布大小的0.9倍的转换器,使椭圆几乎与画布一样大,如下所示:< / p>

XAML

            <Canvas Name="MyCanvas">
            <Ellipse Height="{Binding ElementName=MyCanvas, Path=ActualHeight, Converter={StaticResource MyScaleConverter}}" Width="{Binding ElementName=MyCanvas, Path=ActualWidth, Converter={StaticResource MyScaleConverter}}"></Ellipse>
        </Canvas>

C#

    public class ScaleZeroNine : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return System.Convert.ToDouble(value) * 0.9;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

关于中心问题: 为什么还要用画布?是否有特定原因? Canvas使得使用WPF的全部功能变得困难,因为其布局更像winForms。例如,当使用网格时,它将自动居中,也可以定义为居中

答案 1 :(得分:0)

给椭圆一个渲染变换:

<Ellipse Width="50" Height="50" Fill="Yellow" Stroke="CornflowerBlue" StrokeThickness="                    
    <Ellipse.RenderTransform>
        <TranslateTransform X="-25" Y="-25" /> <!-- center the ellipse -->
    </Ellipse.RenderTransform>
</Ellipse>

答案 2 :(得分:0)

为补充答案,我在自定义值转换器中使用了多重绑定。

public class HalfValueConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values,
                          Type targetType,
                          object parameter,
                          CultureInfo culture)
    {
        if (values == null || values.Length < 2)
        {
            throw new ArgumentException(
                "HalfValueConverter expects 2 double values to be passed" +
                " in this order -> totalWidth, width",
                "values");
        }

        double totalWidth = (double)values[0];
        double width = (double)values[1];
        return (object)((totalWidth - width) / 2);
    }

    public object[] ConvertBack(object value,
                                Type[] targetTypes,
                                object parameter,
                                CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

然后用xaml

 <DockPanel>
        <DockPanel.Resources>
            <local:HalfValueConverter x:Key="HalfValue" />
        </DockPanel.Resources>
        <Canvas x:Name="canvas">
            <Ellipse 
                     Height="{Binding ElementName=canvas, Path=ActualHeight, Converter={StaticResource Escalated}}" 
                Width="{Binding ElementName=canvas, Path=ActualWidth, Converter={StaticResource Escalated}}"
                     Stroke="Red"
                     x:Name="ellipse">
                <Canvas.Left>
                    <MultiBinding Converter="{StaticResource HalfValue}">
                        <Binding ElementName="canvas" Path="ActualWidth" />
                        <Binding ElementName="ellipse" Path="ActualWidth" />
                    </MultiBinding>
                </Canvas.Left>
                <Canvas.Top>
                    <MultiBinding Converter="{StaticResource HalfValue}">
                        <Binding ElementName="canvas" Path="ActualHeight" />
                        <Binding ElementName="ellipse" Path="ActualHeight" />
                    </MultiBinding>
                </Canvas.Top>
            </Ellipse>
        </Canvas>
    </DockPanel>