WPF MVVM ListViewItem的ContextMenu不响应CommandBase事件

问题描述

C# WPF MVVM ListViewItem的ContextMenu的MenuItem中Command绑定的CommandBase事件无响应。

代码逻辑描述

创建了一个MainWindow,在MainWindow中间划定一块区域用以绑定显示其他的View界面;
我在一个View(MonitorView)中创了一个ListView,每一个Items都有几个操作,我将这几个操作定义在了ContextMenu中,当鼠标右击时就显示ContextMenu,点击MenuItem执行对应的操作;

<!-- ContextMenu  -->>
   <ContextMenu x:Key="ContextMenu1">
                        <MenuItem   Header="出厂" FontSize="14" >
                            <MenuItem Header="下行" FontSize="14" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl},Path=DataContext.DownOutTrainCommand}" 
                                                       CommandParameter="{Binding}"/>
                            <Separator/>
                            <MenuItem Header="上行" FontSize="14"/>
                        </MenuItem>
                        <Separator/>
                        <MenuItem Header="去15" FontSize="14" Command="{Binding DataContext.DownOutTrainCommand,RelativeSource={RelativeSource AncestorType={x:Type local:MonitorView}}}" 
                                                       CommandParameter="{Binding }"/>
                        <MenuItem Header="去14" FontSize="14" Command="{Binding RelativeSource={RelativeSource  Mode=FindAncestor, AncestorType=UserControl},Path=DataContext.DownOutTrainCommand}" 
                                                       CommandParameter="{Binding}"  />
                        <MenuItem Header="去13" FontSize="14"  Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.DownOutTrainCommand}" 
                                                       CommandParameter="{Binding}" />
                    </ContextMenu>

ListViewItem 的 ItemContainerStyle的样式

                    <Style x:Key="ListItemType" TargetType="{x:Type ListViewItem}">
                        <Setter Property="Margin" Value="2,10,2,0"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate>

                                    <Border  BorderThickness="1" CornerRadius="5" x:Name="borderground"  >
                                        <Border.Background>
                                            <ImageBrush ImageSource="pack://application:,,,/huainan_railway;component/Assets/Image/train.png" Stretch="UniformToFill"/>
                                        </Border.Background>
                                        <Border.Effect>
                                            <DropShadowEffect Color="#FF1F0F0B" BlurRadius="10" Opacity="0.5"></DropShadowEffect>
                                        </Border.Effect>
                                        <Grid>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="*" />
                                                <RowDefinition Height="4*"/>
                                            </Grid.RowDefinitions>
                                            <Border Grid.Row="0" Background="#2c6cf3" Width="18" Height="18" CornerRadius="9" Margin="2,0,3,-7" BorderBrush="White" BorderThickness="1" HorizontalAlignment="Left">
                                                <TextBlock Text="{Binding StockIndex}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16" Foreground="White"/>
                                            </Border>
                                            <Border Grid.Row="1" Margin="5,0"  Background="Transparent" Name="root" BorderThickness="0,0,0,1" CornerRadius="6">
                                                <Grid>
                                                    <Grid.ColumnDefinitions>
                                                        <ColumnDefinition/>
                                                        <ColumnDefinition/>
                                                    </Grid.ColumnDefinitions>
                                                    <TextBlock Text="{Binding MachineType}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16" Foreground="#FF17FF00" FontWeight="Bold"/>
                                                    <TextBlock Grid.Column="1" Text="{Binding TrainNumber}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16" Foreground="#FF17FF00" FontWeight="Bold"/>
                                                </Grid>
                                            </Border>
                                        </Grid>
                                    </Border>

                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="ContextMenu" Value="{StaticResource ContextMenu1}"/>
                        <Setter Property="ToolTip" Value="{Binding Source={StaticResource ItempInfoTip}}"/>
                    </Style>

ListView 调用

 <ListView  ItemsSource="{Binding trainsSolid.trains5}"  Background="Transparent"  
                       BorderThickness="1,1,1,1"  Style="{StaticResource ListBorderBrush}" 
                       ItemContainerStyle="{StaticResource ListItemType}"
                       Height="55" Width="461" Canvas.Top="610" Canvas.Left="743">
                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal" />
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
            </ListView>

ViewModel中的事件

 //出车 下行
        private CommandBase _downOutTrainCommand;
        public CommandBase DownOutTrainCommand
        {
            get
            {
                if (_downOutTrainCommand == null)
                {
                    _downOutTrainCommand = new CommandBase();
                    _downOutTrainCommand.DoExecute = new Action<object>(obj =>
                    {
                        try
                        {
                            TrainModel trainModel = (TrainModel)obj;
                            //TrainPlanModel trainPlanModel = (TrainPlanModel)obj;
                            //monitorService.UpdatePlanState(3, trainPlanModel.ID);
                            //trainPlanModel.Status = 3;
                            //ObservableCollection<TrainModel> tM = trainsSolid.GetObservable(trainPlanModel.Stock);
                            //monitorService.insertTrain(trainPlanModel, tM);
                            Initialize();
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                    });
                }
                return _downOutTrainCommand;
            }
        }

img

分析

要明确指定数据源 无法通过继承拿到? 但是不知道怎么写;加载ViewModel? 尝试了不行,不知道怎么回事;

                <<ResourceDictionary >
                    <vm:MonitorViewModel x:Key="mvm"/>
                </ResourceDictionary>
<UserControl.DataContext>
        <Binding Source="{StaticResource mvm}"/>
    </UserControl.DataContext>

    <UserControl.ContextMenu>
        <ContextMenu DataContext="{StaticResource mvm}"/>
    </UserControl.ContextMenu>

【以下回答由 ChatGPT 生成】

我很抱歉,但是你没有给出具体的问题。请提供问题后,我将尽力给出相应的解决方案。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

你的DataContext有没有正确地绑定到你的ViewModel上

WPF MVVM模式开发中列表控件内的按钮触发不了command事件
可以参考下


https://www.codenong.com/19447795/

根据你提供的信息,我可以给你以下建议:

  1. 确认你的CommandBase事件是否正确绑定到了MenuItem上。可以通过在MenuItem上打断点或者输出日志来确认Command被触发与否。

  2. 确认你的CommandBase事件是否正确实现,是否有误导致无法被正确触发。

  3. 确认ContextMenu的样式或者模板是否被修改,可能会导致ContextMenu无法正确显示或者Command无法触发。

  4. 如果以上都没有问题,可以尝试在ListViewItem上绑定MouseDown或者PreviewMouseDown事件,然后在事件处理中手动打开ContextMenu,再通过CommandParameter传递ListViewItem对应的数据模型。

  5. 如果还有问题,可以考虑提供更详细的代码或者项目结构,方便进行更深入的分析和解决。

事件没有正确绑定

检查一下事件绑定

WPF中使用MVVM对ContextMenu绑定Command并通过CommandParameter传递参数_晓风-杨柳的博客-CSDN博客 需求:在ListBox中的Item上点击右键时弹出菜单,由于使用MVVM架构,需要把为MenuItem绑定Command,并能把选择的ListBoxItem传递到ViewModel。实现:1、只对被点击的ListItem弹出ContextMenu,只需要建立一个DataTemplate即可,这个比较简单。2、绑定Command,这个是比较麻烦的,查了许多资料,就是利用PlacementTa... https://blog.csdn.net/luo_5458/article/details/86720284?ops_request_misc=&request_id=&biz_id=102&utm_term=C&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-86720284.142^v92^controlT0_1&spm=1018.2226.3001.4187&ydreferer=aHR0cHM6Ly9zby5jc2RuLm5ldC9zby9zZWFyY2g%2Fc3BtPTEwMDAuMjExNS4zMDAxLjQ0OTgmcT1DJTIzJTIwV1BGJTIwTVZWTSUyMExpc3RWaWV3SXRlbSVFNyU5QSU4NENvbnRleHRNZW51JUU3JTlBJTg0TWVudUl0ZW0lRTQlQjglQURDb21tYW5kJUU3JUJCJTkxJUU1JUFFJTlBJUU3JTlBJTg0Q29tbWFuZEJhc2UlRTQlQkElOEIlRTQlQkIlQjYlRTYlOTclQTAlRTUlOTMlOEQlRTUlQkElOTQlRTMlODAlODImdD0mdT0%3D

逻辑回归是一种二分类算法,用于预测二分类问题。逻辑回归的优点是计算速度快,便于理解和实现。本文将介绍使用 Python 构建逻辑回归模型,进行5折交叉验证,每验证一次绘制一条曲线,并最终绘制一条平均 ROC 曲线。

数据集

本文使用的数据集是 UCI 的 Pima Indians Diabetes 数据集,该数据集包含了768个样本,8个特征和1个二元分类。我们将使用该数据集来构建逻辑回归模型。

首先,我们需要导入sklearnpandas库,并读取数据集:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, auc
import numpy as np
import matplotlib.pyplot as plt

# 加载数据集
data = pd.read_csv('diabetes.csv')

# 划分数据集
X = data.iloc[:, :-1].values
y = data.iloc[:, -1].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)

我们将数据集划分为训练集和测试集,测试集占25%。

构建逻辑回归模型

我们将使用LogisticRegression类来构建逻辑回归模型。首先,我们需要实例化该类,并将训练集数据拟合到模型中:

# 构建逻辑回归模型
logreg = LogisticRegression()

# 将训练集拟合到模型中
logreg.fit(X_train, y_train)

5折交叉验证绘制 ROC 曲线

接下来,我们使用cross_val_predict函数来进行5折交叉验证,并使用roc_curve函数来计算 ROC 曲线的参数。

from sklearn.model_selection import cross_val_predict

# 进行5折交叉验证
y_score = cross_val_predict(logreg, X_train, y_train, cv=5, method='predict_proba')

# 计算 ROC 曲线的参数
fpr, tpr, thresholds = roc_curve(y_train, y_score[:, 1])
roc_auc = auc(fpr, tpr)

# 绘制 ROC 曲线
plt.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % roc_auc)

# 绘制对角线
plt.plot([0, 1], [0, 1], 'k--')

# 设置图形参数
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic')
plt.legend(loc='lower right')
plt.show()

我们使用cross_val_predict函数进行5折交叉验证,并将预测的概率值存储在y_score中。然后,我们使用roc_curve函数来计算 ROC 曲线的参数,包括真阳性率和假阳性率。最后,我们使用plt.plot函数将 ROC 曲线绘制出来。

接下来,我们使用for循环进行5次交叉验证,并绘制5条 ROC 曲线:

from sklearn.model_selection import StratifiedKFold

# 进行5折交叉验证
cv = StratifiedKFold(n_splits=5)
mean_tpr = 0.0
mean_fpr = np.linspace(0, 1, 100)
colors = ['blue', 'red', 'green', 'purple', 'black']

# 绘制多条 ROC 曲线
for i, (train, test) in enumerate(cv.split(X_train, y_train)):
    probas_ = logreg.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    fpr, tpr, thresholds = roc_curve(y_train[test], probas_[:, 1])
    mean_tpr += np.interp(mean_fpr, fpr, tpr)
    plt.plot(fpr, tpr, lw=1, color=colors[i],
             label='ROC fold %d (area = %0.2f)' % (i, auc(fpr, tpr)))

# 绘制随机猜测的对角线
plt.plot([0, 1], [0, 1], linestyle='--', lw=2, color='black',
         label='Random guess', alpha=.8)

# 计算和绘制平均 ROC 曲线
mean_tpr /= cv.get_n_splits(X_train, y_train)
mean_tpr[-1] = 1.0
mean_auc = auc(mean_fpr, mean_tpr)
plt.plot(mean_fpr, mean_tpr, color='navy',
         label='Mean ROC (area = %0.2f)' % mean_auc, lw=2)

# 设置图形参数
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate', fontsize=16)
plt.ylabel('True Positive Rate', fontsize=16)
plt.title('Receiver Operating Characteristic (ROC) Curve', fontsize=18)
plt.legend(loc="lower right", fontsize=14)
plt.show()

for循环中,我们使用StratifiedKFold函数进行5折交叉验证,并在每次交叉验证中计算 ROC 曲线的参数。我们使用np.interp函数将所有曲线插值到相同的假阳性率上,然后将真阳性率相加,计算平均值。最后,我们绘制平均 ROC 曲线。

结论

在本文中,我们使用 Python 构建了一个逻辑回归模型,并进行了5折交叉验证。我们绘制了5条 ROC 曲线,并计算了平均 ROC 曲线。通过绘制 ROC 曲线,我们可以评估模型的性能,并选择最佳的阈值来进行分类。

如果你在ListView中使用了一些自定义的视图或模板,那么需要将这些视图或模板的点击事件与ContextMenu的点击事件解耦。这样,点击事件就不会互相干扰。
其次,检查是否有其他的事件处理器阻止了右键菜单的触发。例如,某些鼠标事件处理器可能会阻止右键菜单的弹出。