【问题标题】:Clicking listbox usercontrol items in wpf单击wpf中的列表框用户控件项
【发布时间】:2016-01-07 02:53:47
【问题描述】:

所以在我的应用程序中,我必须指定类对象的类型,CirclesRectangles。两者都是使用 ListBox 绘制的。矩形是绿色的,圆圈是黄色的。

您可能会问我为什么要使用列表框来显示它们。此处的好处是免费赠品,使用户可以在如图所示的窗口内单击时选择项目。

我需要帮助的问题,它们都与单击/选择的同一主题有关。

更新日期:2016 年 1 月 7 日

  1. 由于所有列表框项都显示为带有 Box Shaped 命中测试区域,因此会出现此类问题。用户想要选择矩形,但他们却选择了黄色圆圈。

问题(左)|期望的目标(右)

  1. 这导致了我的最后一个问题,即黄色圆圈形状的命中测试。当用户单击负区域时,它实际上不应该选择该行。只有当用户光标直接位于该行上方时,它才应该选择该行。如右图所示。理想情况下,当它被选中时,突出显示指示更适合突出显示的形状,而不是一个巨大的矩形。

就代码而言,它相当短,为了简单起见,我将它组合成较小的文件。

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        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:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        Background="Gray">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>

    <Window.Resources>

        <DataTemplate DataType="{x:Type local:RectangleViewModel}" >
            <Border Cursor="Hand" Background="Green" CornerRadius="4" Width="100" Height="100" Margin="10"/>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:CircleViewModel}" >
            <Path Data="M0,0 C0,0  10,100  100,100" Stroke="Gold" StrokeThickness="5" Cursor="Hand"/>
        </DataTemplate>

    </Window.Resources>

    <!-- Presents the Rectangles -->
    <ListBox x:Name="listBox" ItemsSource="{Binding Items}" SelectionMode="Extended" Background="Transparent">

        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>

        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem" >
                <Setter Property="Canvas.Left" Value="{Binding X}" />
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
            </Style>
        </ListBox.ItemContainerStyle>

        <ListBox.Resources>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            </Style>
        </ListBox.Resources>

    </ListBox>

</Window>

MainWindowViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApplication1
{
    public class MainWindowViewModel
    {

        private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
        public ObservableCollection<ItemViewModel> Items { get { return items; } }

        public MainWindowViewModel()
        {
            // Populate the view model with some example data.
            items.Add(new RectangleViewModel(250, 200));
            items.Add(new RectangleViewModel(320, 370));
            items.Add(new RectangleViewModel(100, 50));
            items.Add(new RectangleViewModel(350, 25));
            items.Add(new RectangleViewModel(70, 270));

            items.Add(new CircleViewModel(20, 20));
            items.Add(new CircleViewModel(300, 270));
            items.Add(new CircleViewModel(350, 100));
            items.Add(new CircleViewModel(50, 315));
            items.Add(new CircleViewModel(100, 170));
        }
    }

    public class ItemViewModel
    {
        // position coordinates
        public double X { get; set; }
        public double Y { get; set; }
    }

    public class CircleViewModel : ItemViewModel
    {
        // Constructors
        public CircleViewModel(double x, double y)
        {
            this.X = x;
            this.Y = y;
        }
    }
    public class RectangleViewModel : ItemViewModel
    {
        // Constructors
        public RectangleViewModel(double x, double y)
        {
            this.X = x;
            this.Y = y;
        }
    }
}

更新 - 更接近 - 尝试 #2

现在我可以在单击的 hitTest 正确的情况下拖动和移动画布中的项目。但是由于某种原因,当我尝试在底部的蓝色画布中移动形状时,它们不会移动,而它们确实会在顶部移动。试试看....

MainWindow.xaml.cs

   using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    namespace DragShapes
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }

            bool captured = false;
            double x_shape, x_canvas, y_shape, y_canvas;
            UIElement source = null;

            private void shape_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                Console.WriteLine("MouseDown--Pressed");
                source = (UIElement)sender;
                Mouse.Capture(source);
                captured = true;
                x_shape = Canvas.GetLeft(source);
                x_canvas = e.GetPosition(LayoutRoot).X;
                y_shape = Canvas.GetTop(source);
                y_canvas = e.GetPosition(LayoutRoot).Y;
            }

            private void shape_MouseMove(object sender, MouseEventArgs e)
            {
                if (captured)
                {
                    Console.WriteLine("MouseMove--Pressed");
                    double x = e.GetPosition(LayoutRoot).X;
                    double y = e.GetPosition(LayoutRoot).Y;
                    x_shape += x - x_canvas;
                    Canvas.SetLeft(source, x_shape);
                    x_canvas = x;
                    y_shape += y - y_canvas;
                    Canvas.SetTop(source, y_shape);
                    y_canvas = y;
                }
            }

            private void 

shape_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("MouseUp--Pressed");
            Mouse.Capture(null);
            captured = false;
        }
    }
}

MainWindowViewModel.cs

  using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;


    namespace DragShapes
    {
        public class MainWindowViewModel
        {

            private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
            public ObservableCollection<ItemViewModel> Items { get { return items; } }

            public MainWindowViewModel()
            {
                // Populate the view model with some example data.
                items.Add(new RectangleViewModel(250, 200));
                items.Add(new RectangleViewModel(320, 370));
                items.Add(new RectangleViewModel(100, 50));
                items.Add(new RectangleViewModel(350, 25));
                items.Add(new RectangleViewModel(70, 270));

                items.Add(new CircleViewModel(20, 20));
                items.Add(new CircleViewModel(300, 270));
                items.Add(new CircleViewModel(350, 100));
                items.Add(new CircleViewModel(50, 315));
                items.Add(new CircleViewModel(100, 170));
            }
        }

        public class ItemViewModel : INotifyPropertyChanged
        {
            // position coordinates
            private double x = 0;
            public double X
            {
                get { return x; }
                set
                {
                    if (x != value)
                    {
                        x = value;
                        OnPropertyChanged("X");
                    }
                }
            }

            private double y = 0;
            public double Y
            {
                get { return y; }
                set
                {
                    if (y != value)
                    {
                        y = value;
                        OnPropertyChanged("Y");
                    }
                }
            }

            protected void OnPropertyChanged(string name)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
        }

        public class CircleViewModel : ItemViewModel
        {
            // Constructors
            public CircleViewModel(double x, double y)
            {
                this.X = x;
                this.Y = y;
            }
        }
        public class RectangleViewModel : ItemViewModel
        {
            // Constructors
            public RectangleViewModel(double x, double y)
            {
                this.X = x;
                this.Y = y;
            }
        }


    }

MainWindow.xaml

<Window x:Class="DragShapes.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="600" Width="600"
        xmlns:local="clr-namespace:DragShapes">

    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>

    <Window.Resources>

        <DataTemplate DataType="{x:Type local:RectangleViewModel}" >
            <Rectangle Cursor="Hand" Fill="Green" Width="100" Height="100" Margin="10"
                    MouseLeftButtonDown="shape_MouseLeftButtonDown" 
                    MouseMove="shape_MouseMove" 
                    MouseLeftButtonUp="shape_MouseLeftButtonUp"/>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:CircleViewModel}" >
            <Path Data="M0,0 C0,0  10,100  100,100" Stroke="Gold" StrokeThickness="5" Cursor="Hand"
                  MouseLeftButtonDown="shape_MouseLeftButtonDown" 
                  MouseLeftButtonUp="shape_MouseLeftButtonUp"
                  MouseMove="shape_MouseMove"/>
        </DataTemplate>

    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Canvas x:Name="LayoutRoot" Background="White">

            <Ellipse Fill="Blue" HorizontalAlignment="Center" Height="100" Stroke="Black" VerticalAlignment="Center" Width="100" Canvas.Left="200" Canvas.Top="100"
                     MouseLeftButtonDown="shape_MouseLeftButtonDown" 
                     MouseMove="shape_MouseMove" 
                     MouseLeftButtonUp="shape_MouseLeftButtonUp" />

            <Rectangle Fill="Red" Height="100" Stroke="Black" Width="100" HorizontalAlignment="Left" VerticalAlignment="Bottom" Canvas.Left="10" Canvas.Top="10" 
                       MouseLeftButtonDown="shape_MouseLeftButtonDown" 
                       MouseLeftButtonUp="shape_MouseLeftButtonUp"
                       MouseMove="shape_MouseMove"/>

        </Canvas>

        <Canvas Grid.Row="1"  Background="White" >

            <ItemsControl ItemsSource="{Binding Path=Items}" >

                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas Background="LightBlue" Width="500" Height="500"  />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>

                <!--<ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Ellipse Fill="Green" Width="25" Height="25" 
                                 MouseLeftButtonDown="shape_MouseLeftButtonDown" 
                                 MouseLeftButtonUp="shape_MouseLeftButtonUp"
                                 MouseMove="shape_MouseMove"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>-->

                <ItemsControl.ItemContainerStyle>
                    <Style>
                        <Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
                        <Setter Property="Canvas.Left" Value="{Binding Path=X}" />
                    </Style>
                </ItemsControl.ItemContainerStyle>
            </ItemsControl>

            <!--<ItemsControl ItemsSource="{Binding Path=Items}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemContainerStyle>
                    <Style TargetType="ContentPresenter">
                        <Setter Property="Canvas.Left" Value="{Binding X}"/>
                        <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>
            </ItemsControl>-->

            <!--<ItemsControl ItemsSource="{Binding Path=Items}">
                --><!--<ItemsControl.ItemContainerStyle>
                    <Style TargetType="ContentPresenter">
                        <Setter Property="Canvas.Left" Value="{Binding X}"/>
                        <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>--><!--
            </ItemsControl>-->
        </Canvas>
    </Grid>
</Window>

【问题讨论】:

  • 只是大声思考,但也许可以通过将 IsHitTestVisible 设置为 false 来将 ItemTemplate 内的网格和 ItemContainerStyle 设置为 false。并为实际路径/边框设置为 true。
  • 我会尝试并报告。
  • @TomWuyts 否定,这行不通,但听起来是个好主意。
  • 我看到您正在使用 WPF,您可以使用像 Snoop(一个 xaml 检查器)这样的工具来监控单击该区域时触发的事件(以及在哪些元素上)。这样,您可以查明处理点击事件的实际元素并在那里忽略它,或者将该元素的 IsHitTestVisible 设置为 false。
  • 有没有办法让光标下的所有元素不管它们在 xaml 中的什么位置?

标签: c# wpf xaml listbox


【解决方案1】:

默认 ListBoxItem 模板具有活动背景,因此我们需要像这样重置模板:

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem" >
        <Setter Property="Canvas.Left" Value="{Binding X}" />
        <Setter Property="Canvas.Top" Value="{Binding Y}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <ContentPresenter/>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="LightBlue"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ListBox.ItemContainerStyle>

然后,我们需要修改矩形和圆形模板,以获得选择效果,当它们被选中时:

<DataTemplate DataType="{x:Type local:RectangleViewModel}" >
    <Grid>
        <Border CornerRadius="4"
                Background="{Binding 
                    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, 
                    Path=Background}"/>
        <Border Cursor="Hand" Background="Green" CornerRadius="4" Width="100" Height="100" Margin="10"/>
    </Grid>
</DataTemplate>

<DataTemplate DataType="{x:Type local:CircleViewModel}" >
    <Grid>
        <Path Data="M0,0 C0,0  10,100  100,100" StrokeThickness="15"
              Stroke="{Binding 
                RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, 
                Path=Background}"/>
        <Path Data="M0,0 C0,0  10,100  100,100" Stroke="Gold" StrokeThickness="5" Cursor="Hand"/>
    </Grid>
</DataTemplate>

【讨论】:

  • 查看我的第二次尝试。我能够进行一些拖动工作,但是当我使用数据模板时它停止工作。
  • 太棒了!您已 100% 纠正了选择问题。太感谢了。最后是让拖动功能起作用。
  • 关于 ATTEMPT #2 上的拖放操作。
  • 您在“ItemsContainerStyle”上设置了“Canvas.Top”属性,它不会影响“ItemViewModel”。因此,“Canvas.GetTop(source)”返回 NaN,因为事件源不是“ItemsContainerStyle”。您需要使用方法 e.GetPosition(source) 来测量发件人的位置;并将目标位置设置为 var target = source.DataContext as ItemViewModel; if (target != null){ var diff = e.GetPosition(source) - mouseStartPosition;目标.X += 差异.X;目标.Y += 差异。}
猜你喜欢
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多