【问题标题】:How to center shapes based on Canvas size using MVVM如何使用 MVVM 根据画布大小居中形状
【发布时间】:2019-01-11 03:26:30
【问题描述】:

有一个显示圆圈的代码(使用宽度和高度相等的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>

这会产生:

如何将 xamlcode behind 更改为居中并将圆设置为几乎与画布一样的大小?:

【问题讨论】:

    标签: c# wpf xaml canvas mvvm


    【解决方案1】:

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

    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 使使用 WPFs 的全部功能变得困难,因为它的安排更像是 winForms。例如,当使用网格时,它会自动居中,也可以定义为这样做

    【讨论】:

    • 很好的答案丹尼斯,我想使用 MVVM 来制作东西,所以在屏幕中心初始化所有形状的最佳方法是什么,只需添加一个网格,如:'定义网格定义,如: ´ 就足够了,或者如何在中心初始化所有元素?
    • 不确定我是否真的理解你的问题......但如果你真的想走简单的路线并避免使用转换器,你可以这样做: 这不是最优雅的方式,但也许有帮助...如果不是,请尝试以不同的方式进行
    • *ask 一般来说,不定义网格的大小是有意义的,因此一旦您更改窗口大小,整个控件就会保持可伸缩性。MVVM 与这种特定的布局绝对无关。你确定你了解 MVVM 的概念吗?
    • 嗯,问题是如何初始化在视图模型中创建的对象,我的意思是,例如在屏幕中心创建它们,这是因为,在 MainViewModel() 上我添加了如下形状:形状 = 新的 ObservableCollection {...}
    • 好的,当使用 MVVM 模式时,您将获得实际的视图,例如 MainWindow、它的代码隐藏和 ViewModel。在 ViewModel 中你想拥有应用程序代码,这意味着你的程序的实际功能。您不想直接从这里修改 GUI,因为这会破坏 MVVM 模式。这也意味着您不想从这里初始化 GUI 组件。我想你想要做的是让圆圈和线条在运行时出现并隐藏......
    【解决方案2】:

    给椭圆一个渲染变换:

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

    【讨论】:

    • 不完全理解,我怎样才能使宽度和高度可变,并让它们成为例如几乎画布的区域?也许绑定一个画布属性或类似的东西
    【解决方案3】:

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

    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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-11
      • 1970-01-01
      • 1970-01-01
      • 2016-06-15
      • 1970-01-01
      • 1970-01-01
      • 2016-12-10
      • 2016-09-22
      相关资源
      最近更新 更多