【问题标题】:How to draw grid lines that scale?如何绘制缩放的网格线?
【发布时间】:2016-05-03 10:32:57
【问题描述】:

我有一个画布,我想给它网格线作为背景,但我希望有恒定数量的网格线将画布分成大小相等的部分,而不是只有等间距的网格-行。我希望在用户调整画布大小时保留它。

我应该怎么做?

【问题讨论】:

    标签: c# .net wpf user-interface


    【解决方案1】:

    这是一个基于画布后面的两个 wpf ListView 控件的解决方案(一个用于行,第二个用于列)。 ListView 控件相关列的内容是一个矩形。

    更新版本 - 托管网格线控件。您可以在此处管理网格线的数量及其可见性。

    Xaml 代码 - 网格线控件:

    <UserControl x:Class="CAnvasWithGrid.GridLineControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:canvasWithGrid="clr-namespace:CAnvasWithGrid"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" x:Name="This">
    
    <Grid x:Name="LayoutRoot">
        <Grid.Resources>
            <Style TargetType="ListView">
                <Setter Property="Background" Value="Transparent"/>
            </Style>
            <Style x:Key="ListViewItemStyle" TargetType="ListViewItem">
                <Setter Property="Background" Value="Transparent"/>
            </Style>
            <DataTemplate x:Key="InnerListviewDataTemplate" DataType="{x:Type canvasWithGrid:CellModel}">
                <Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                           Margin="0" StrokeDashArray="4" Stroke="Black" StrokeThickness="0.5" Fill="Transparent"/>
            </DataTemplate>
            <DataTemplate x:Key="ListviewDataTemplate" DataType="{x:Type canvasWithGrid:RowModel}">
                <ListView ItemsSource="{Binding CellModels}" BorderBrush="#00FFFFFF" BorderThickness="0" Margin="0"
                                          HorizontalContentAlignment="Stretch"
                                          VerticalContentAlignment="Stretch"
                                          VerticalAlignment="Stretch" 
                                          HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden">
                    <ListView.ItemsPanel>
                        <ItemsPanelTemplate>
                            <UniformGrid Columns="{Binding CellModels, Converter={canvasWithGrid:CollectionLength2NumberConverter}}"></UniformGrid>
                        </ItemsPanelTemplate>
                    </ListView.ItemsPanel>
                    <ListView.ItemContainerStyle>
                        <Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemStyle}">
                            <Setter Property="Margin" Value="0"></Setter>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ListViewItem">
                                        <ContentPresenter Content="{TemplateBinding Content}" Margin="0"
                                                  ContentTemplate="{StaticResource InnerListviewDataTemplate}" />
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="ContentTemplate" Value="{StaticResource InnerListviewDataTemplate}"/>
                        </Style>
                    </ListView.ItemContainerStyle>
                </ListView>
            </DataTemplate>
        </Grid.Resources>
        <ListView ItemsSource="{Binding ElementName=This, Path=RowModels}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="{Binding ElementName=This, Path=RowModels, Converter={canvasWithGrid:CollectionLength2NumberConverter}}"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemStyle}">
                    <Setter Property="Margin" Value="0"></Setter>
                    <Setter Property="HorizontalAlignment" Value="Stretch"/>
                    <Setter Property="VerticalAlignment" Value="Stretch"/>
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListViewItem">
                                <ContentPresenter Content="{TemplateBinding Content}" Margin="-1"
                                                  ContentTemplate="{StaticResource ListviewDataTemplate}" />
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="ContentTemplate" Value="{StaticResource ListviewDataTemplate}"/>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
    </Grid>
    

    网格线控件 - 代码隐藏

        /// <summary>
    /// Interaction logic for GridLineControl.xaml
    /// </summary>
    public partial class GridLineControl : UserControl
    {
        public GridLineControl()
        {
            InitializeComponent();
        }
    
        public static readonly DependencyProperty NumberOfColumnsProperty = DependencyProperty.Register(
            "NumberOfColumns", typeof (int), typeof (GridLineControl), new PropertyMetadata(default(int), NumberOfColumnsChangedCallback));
    
        private static void NumberOfColumnsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            var numberOfRows = (int)dependencyObject.GetValue(NumberOfRowsProperty);
            var numberOfColumns = (int)args.NewValue;
            if (numberOfColumns == 0 || numberOfRows == 0) return;
            var rowModelsCollection = GetRowModelsCollection(numberOfRows, numberOfColumns);
            dependencyObject.SetValue(RowModelsProperty, rowModelsCollection);
        }
    
        public int NumberOfColumns
        {
            get { return (int) GetValue(NumberOfColumnsProperty); }
            set { SetValue(NumberOfColumnsProperty, value); }
        }
    
        public static readonly DependencyProperty NumberOfRowsProperty = DependencyProperty.Register(
            "NumberOfRows", typeof (int), typeof (GridLineControl), new PropertyMetadata(default(int), NumberOfRowsChangedCallback));
    
        private static void NumberOfRowsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            var numberOfRows = (int)args.NewValue;
            var numberOfColumns = (int)dependencyObject.GetValue(NumberOfColumnsProperty);
            if(numberOfColumns == 0 || numberOfRows == 0) return;
            var rowModelsCollection = GetRowModelsCollection(numberOfRows, numberOfColumns);
            dependencyObject.SetValue(RowModelsProperty, rowModelsCollection);
        }
    
        private static ObservableCollection<RowModel> GetRowModelsCollection(int numberOfRows, int numberOfColumns)
        {
            var rowModelsCollection = new ObservableCollection<RowModel>();
            for (var i = 0; i < numberOfRows; i++)
            {
                rowModelsCollection.Add(new RowModel(numberOfColumns) {Position = (i + 1).ToString()});
            }
            return rowModelsCollection;
        }
    
        public int NumberOfRows
        {
            get { return (int) GetValue(NumberOfRowsProperty); }
            set { SetValue(NumberOfRowsProperty, value); }
        }
    
        public static readonly DependencyProperty RowModelsProperty = DependencyProperty.Register("RowModels",
            typeof(ObservableCollection<RowModel>), typeof(GridLineControl),
            new PropertyMetadata(default(ObservableCollection<RowModel>)));
    
        public ObservableCollection<RowModel> RowModels
        {
            get { return (ObservableCollection<RowModel>)GetValue(RowModelsProperty); }
            private set { SetValue(RowModelsProperty, value); }
        }
    }
    

    型号:

    public class RowModel:BaseGridMember
    {
        public RowModel(int numberOfCellsInRow)
        {
            CellModels = new ObservableCollection<CellModel>();
            for (int i = 0; i < numberOfCellsInRow; i++)
            {
                CellModels.Add(new CellModel{Position = (i+1).ToString()});
            }
        }
        public ObservableCollection<CellModel>  CellModels { get; set; }
    }
    
    public class CellModel:BaseGridMember
    {
    
    }
    
    public class BaseGridMember:BaseObservableObject
    {
        private string _position;
    
        public string Position
        {
            get { return _position; }
            set
            {
                _position = value;
                OnPropertyChanged();
            }
        }
    }
    

    主窗口 xaml 代码 - 如您所见,这里是 ImageContol 而不是 Canvas,但您可以替换它:

    <Window x:Class="CAnvasWithGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:canvasWithGrid="clr-namespace:CAnvasWithGrid"
        Title="MainWindow" Height="525" Width="525" x:Name="This">
    
    <Grid Tag="{Binding ElementName=This}">
        <Grid.Resources>
            <BooleanToVisibilityConverter x:Key="Bool2VisConvKey" />
        </Grid.Resources>
        <Grid.ContextMenu>
            <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="Show Grid Lines" Command="{Binding ShowGridLinesCommand}"/>
            </ContextMenu>
        </Grid.ContextMenu>
        <Image Source="Resources/Koala.jpg" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseDown="UIElement_OnMouseDown"/>
        <canvasWithGrid:GridLineControl NumberOfRows="50" NumberOfColumns="50" 
                                        IsHitTestVisible="False" Visibility="{Binding ElementName=This, Path=AreGridLineVisible, Converter={StaticResource Bool2VisConvKey}, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
    

    后面的主窗口代码:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ShowGridLinesCommand = new RelayCommand(ShowGridLineManageCommand);
            AreGridLineVisible = true;
        }
    
        private void ShowGridLineManageCommand()
        {
            AreGridLineVisible = !AreGridLineVisible;
        }
    
        public static readonly DependencyProperty AreGridLineVisibleProperty = DependencyProperty.Register(
            "AreGridLineVisible", typeof (bool), typeof (MainWindow), new PropertyMetadata(default(bool)));
    
        public bool AreGridLineVisible
        {
            get { return (bool) GetValue(AreGridLineVisibleProperty); }
            set { SetValue(AreGridLineVisibleProperty, value); }
        }
    
        public static readonly DependencyProperty ShowGridLinesCommandProperty = DependencyProperty.Register(
            "ShowGridLinesCommand", typeof (ICommand), typeof (MainWindow), new PropertyMetadata(default(ICommand)));
    
        public ICommand ShowGridLinesCommand
        {
            get { return (ICommand) GetValue(ShowGridLinesCommandProperty); }
            set { SetValue(ShowGridLinesCommandProperty, value); }
        }
    
    
    
        private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
        {
    
        }
    }
    

    外观:

    【讨论】:

    • @GregRos 这里是更新版本。现在您可以将网格线控件用作 UserControl。当您的画布可以在网格线上方或下方时,您还可以管理网格线的可见性和行/列数。
    【解决方案2】:

    听起来像是带有自定义绘图的自定义控件的候选者。如果出于性能原因您期望有许多网格线,您并不想使用多个 FrameworkElement,例如“Line”。

    因此,您将创建一个 customControl GridLinesControl 并覆盖 OnRender 方法。您可以使用属性 ActualWidth 和 ActualHeight 获取组件的实际宽度和高度,除以您想要的网格线数并使用 drawingContext.DrawLine 绘制线条。

    最简单的方法是添加您在画布下方制作的 GridLinesControl,占用相同的空间(因此它具有正确的 ActualWidth 和 ActualHeight),如下所示:

    <Grid>
        <myControls:GridLinesControl/>
        <Canvas ... />
    </Grid>
    

    所以它总是在下面。

    【讨论】:

      猜你喜欢
      • 2013-01-21
      • 1970-01-01
      • 2022-08-19
      • 2018-09-18
      • 2013-09-26
      • 2020-10-08
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      相关资源
      最近更新 更多