弹出窗口在列表视图 WPF 中多次显示

Popup shows up multiple times in listview WPF

提问人:Gokhan 提问时间:10/29/2023 更新时间:10/30/2023 访问量:153

问:

我正在 WPF 中处理一个项目。我有 listview 控件,在该 listview 中,我有文本块和按钮作为模板控件。

我希望当用户单击按钮时,弹出窗口打开并显示所选项目的信息。但是当单击按钮时,弹出窗口会多次显示(项目计数)。

这些是 XAML 代码。

<Window x:Class="TestingPopupShow.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:vm="clr-namespace:TestingPopupShow.ViewModel"
        xmlns:local="clr-namespace:TestingPopupShow"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="400">

    <Window.Resources>
        <ResourceDictionary>
            <vm:ItemInfosPopupVM x:Key="vm"/>
        </ResourceDictionary>
    </Window.Resources>
    
   
        <Grid   x:Name="SubGrid" Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="20"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <ListView  x:Name="listView" 
              Grid.Row="1"
              Margin="0 5 0 0">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Border x:Name="debugPanel"  CornerRadius="3" BorderBrush="Black" BorderThickness="0.5"  Grid.Column="0" Grid.RowSpan="2">
                            <Grid DataContext="{StaticResource vm}"  x:Name="listViewGrid" Margin="1"
                      HorizontalAlignment="Center"
                      Background="#F2F2F2">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="80"/>
                                    <ColumnDefinition Width="120"/>
                                    <ColumnDefinition Width="25"/>
                                </Grid.ColumnDefinitions>

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

                                <StackPanel Grid.Column="0">
                                    <TextBlock Text="W01"
                                               FontSize="12"
                                               Margin="5"
                                               FontFamily="Tohama"/>
                                    <TextBlock Text="1 pcs"
                                               FontSize="12"
                                               Margin="5"
                                               FontFamily="Tohama"/>


                                    <TextBlock Text="1200 X 1600"
                                               FontSize="12"
                                               Margin="5"
                                               FontFamily="Tohama"/>
                                </StackPanel >

                                <DockPanel x:Name="imageDockPanel" Grid.Column="1">

                                </DockPanel >
                                <Button Grid.Column="2"
                                            BorderThickness="0"
                                                Command="{Binding showPopupCommand}"
                                            x:Name="buttonOpen" 
                                            Content=">"
                                            HorizontalAlignment="Stretch"/>
                                <Popup x:Name="itemInfosPopup" 
                                                   
                                              StaysOpen="False"
                                               Placement="Mouse"
                                               IsOpen="{Binding PopupVis, UpdateSourceTrigger=PropertyChanged}"
                                               AllowsTransparency="True">
                                    <Border BorderBrush="Black" BorderThickness="2">
                                        <StackPanel Background="White" Width="200" Height="300">

                                        </StackPanel>
                                    </Border>
                                </Popup>
                            </Grid>
                        </Border>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>
</Window>

这是 ViewModel 类

public class ItemInfosPopupVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool popupVis;
    public bool PopupVis
    {
        get { return popupVis; }
        set
        {
            popupVis = value;
            OnPropertyChanged("PopupVis");
        }
    }
    
    public ShowPopupCommand showPopupCommand { get; set; }
    public ItemInfosPopupVM()
    {
        PopupVis = false;
        showPopupCommand = new ShowPopupCommand(this);
    }
       
    public void ShowPopup()
    {
        if (PopupVis)
        {
            PopupVis = false;
        }
        else
        {
            PopupVis = true;
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

这些是命令类:

public class ShowPopupCommand : ICommand
{
    public ItemInfosPopupVM ViewModel { get; set; }
    public event EventHandler CanExecuteChanged;
    public ShowPopupCommand(ItemInfosPopupVM vm)
    {
        ViewModel = vm;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        ViewModel.ShowPopup();
    }
}

这些是用于在主窗口视图中进行测试的临时代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Temporary codes. Testing listview.
        for (int i = 0; i < 5; i++)
        {
            listView.Items.Add("Item " + i.ToString());
        }
    }
}

我想我在 XAML 中的错误位置创建了弹出控件。但我无法弄清楚。

enter image description here

C# WPF 模板 ListView 弹出窗口

评论


答:

1赞 EldHasp 10/29/2023 #1

错误地实现了数据上下文 (ViewModel) 和命令。
下面是一个基于基类的示例:BaseInpc 和 RelayCommand

    public class ItemInfosPopupVM : BaseInpc
    {
        private bool popupVis;
        private string title = string.Empty;

        public bool PopupVis { get => popupVis; set => Set(ref popupVis, value); }

        public string Title { get => title; set => Set(ref title, value ?? string.Empty); }
    }
    public class ItemsVM
    {
        public ObservableCollection<ItemInfosPopupVM> ItemInfos { get; } = new();

        public ItemsVM()
        {
            // Temporary codes. Testing listview.
            for (int i = 0; i < 5; i++)
            {
                ItemInfos.Add(new ItemInfosPopupVM { Title = "Item " + i.ToString() });
            }

            InvertPopupVis = new RelayCommand<ItemInfosPopupVM>
            (
                item => item.PopupVis = !item.PopupVis
            );
        }

        public RelayCommand<ItemInfosPopupVM> InvertPopupVis { get; }
    }
<Window x:Class="TestingPopupShow.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:vm="clr-namespace:TestingPopupShow.ViewModel"
        xmlns:local="clr-namespace:TestingPopupShow"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="400"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <vm:ItemsVM x:Key="vm"/>
    </Window.Resources>


    <Grid   x:Name="SubGrid" Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding ItemInfos}"
              Grid.Row="1"
              Margin="0 5 0 0">
            <ListBox.ItemTemplate>
                <DataTemplate DataType="{x:Type vm:ItemInfosPopupVM}">
                    <Border CornerRadius="3" BorderBrush="Black" BorderThickness="0.5"  Grid.Column="0" Grid.RowSpan="2">
                        <Grid Margin="1"
                              HorizontalAlignment="Center"
                              Background="#F2F2F2">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="80"/>
                                <ColumnDefinition Width="120"/>
                                <ColumnDefinition Width="25"/>
                            </Grid.ColumnDefinitions>

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

                            <StackPanel Grid.Column="0">
                                <TextBlock Text="{Binding Title, StringFormat='{}{0} - W01'}"
                                           FontSize="12"
                                           Margin="5"
                                           FontFamily="Tohama"/>
                                <TextBlock Text="1 pcs"
                                           FontSize="12"
                                           Margin="5"
                                           FontFamily="Tohama"/>


                                <TextBlock Text="1200 X 1600"
                                           FontSize="12"
                                           Margin="5"
                                           FontFamily="Tohama"/>
                            </StackPanel >

                            <DockPanel x:Name="imageDockPanel" Grid.Column="1">

                            </DockPanel >
                            <Button Grid.Column="2"
                                    BorderThickness="0"
                                    Command="{Binding InvertPopupVis, Source={StaticResource vm}}"
                                    CommandParameter="{Binding}"
                                    Content=">"
                                    HorizontalAlignment="Stretch"/>
                            <Popup StaysOpen="False"
                                   Placement="Mouse"
                                   IsOpen="{Binding PopupVis, UpdateSourceTrigger=PropertyChanged}"
                                   AllowsTransparency="True">
                                <Border BorderBrush="Black" BorderThickness="2">
                                    <StackPanel Background="White" Width="200" Height="300">

                                    </StackPanel>
                                </Border>
                            </Popup>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

enter image description here

评论

0赞 Gokhan 10/30/2023
我从您共享的链接添加了 BaseInc 和 RelayCommand 类。然后我用你的代码更改我的代码并运行程序。我单击了按钮,但没有显示弹出窗口。
1赞 EldHasp 10/30/2023
@Gokhan,我的代码已经过我的验证。结果在我的答案的屏幕截图中。也许您错误地复制了某些内容或出于其他原因。如果可能,请将整个解决方案上传到 GitHub。我会检查的。
0赞 Gokhan 10/30/2023
我更详细地检查了代码并再次尝试。它工作得很好。非常感谢。
1赞 Ferid Š. Sejdović 10/30/2023 #2

像这样从控件中提取弹出控件,并在控件中添加 Event,如下所示:ListViewselection changedListView

<Grid x:Name="SubGrid" Grid.Column="1">
<Grid.RowDefinitions>
    <RowDefinition Height="20"/>
    <RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView SelectionChanged="listView_SelectionChanged" ... />
 <Popup x:Name="itemInfosPopup" 
        StaysOpen="False"
        Placement="Mouse"
        IsOpen="{Binding PopupVis, UpdateSourceTrigger=PropertyChanged}"
        AllowsTransparency="True">
        <Border BorderBrush="Black" BorderThickness="2">
           <StackPanel Background="White" Width="200" Height="300">

           </StackPanel>
 </Border>
 </Popup>

现在,在这种情况下,您需要仅为选定的项目打开弹出控件,而不是为所有项目打开弹出控件(因为这是您的要求):

private void listView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (listView.SelectedItem != null)
    {
        itemInfosPopup.IsOpen = true;
    }
    else
    {
        itemInfosPopup.IsOpen = false;
    }
   
}

就是这样。

重要提示:这只会打开一个弹出窗口,现在您需要在选择更改时处理关闭和打开另一个弹出窗口(当您单击并选择其他项目时)。您的任务是为选定项目打开一个:)并且要小心,因为弹出控件处理可能很棘手。

评论

0赞 Gokhan 10/30/2023
非常感谢关注。根据您的建议,选择项目时会出现弹出窗口。但是我想要点击按钮。
1赞 Ferid Š. Sejdović 10/30/2023
我将其指向选择更改事件,因为我想展示您应该关注的位置(您的问题是如何打开一个计数次数的单个 instread),现在很容易将逻辑切换到其他任何地方,最后要小心在更改为 ListView 控件中的另一个元素时将 IsOpen = False 放在哪里。@Gokhan
1赞 Gokhan 10/30/2023
谢谢,我会听从你的警告。现在我将使用@EldHasp解决方案并尝试将数据绑定到列表框。看起来并不像你说的那样容易。
1赞 Ferid Š. Sejdović 10/31/2023
关于打开/关闭处理(简而言之)。打开现在是清晰的,您必须在单击其他选定项目并编写一些与之相关的验证代码时关闭它,并注意定义应该关闭弹出窗口的内容。例如,如果您打开弹出控件并且它保持打开状态,我认为您应该进行一些鼠标向上或预先鼠标向上按钮单击事件并关闭它,然后打开一个被选中的新控件,如果您有兴趣,我可以稍后更新一些代码?@Gokhan
1赞 Gokhan 10/31/2023
随着EldHasp解决方案的运行良好,无需实施关闭。如果单击外部或其他项目,它会自动关闭。目前没有问题。谢谢关注。