gpt4 book ai didi

c# - 从枚举值列表创建可检查上下文菜单的通用方法

转载 作者:可可西里 更新时间:2023-11-01 08:09:04 24 4
gpt4 key购买 nike

我想创建一个上下文菜单,其中一个 menuItem 将是一个可以在枚举值中进行选择的子菜单。

我不想将枚举中的任何值硬编码到 xaml 中,因为我希望任何枚举值更改都会自动反射(reflect)在 UI 中,而无需任何干预。

我希望我的菜单是一个没有任何伪影的常规上下文菜单(我的意思是外观应该与常规 ContextMenu 一样)。

我尝试了很多方法都没有成功。我的每个试验总是遗漏一些东西,但主要遗漏的部分似乎是一个可以绑定(bind)到某些东西的 converterParamter。

我是红色的:

这是我多次试验及相关代码:

<Window x:Class="WpfContextMenuWithEnum.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfContextMenuWithEnum="clr-namespace:WpfContextMenuWithEnum"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:converter="clr-namespace:WpfContextMenuWithEnum.Converter"
Title="MainWindow" Height="350" Width="525"
Name="MyWindow">
<Window.DataContext>
<wpfContextMenuWithEnum:MainWindowModel></wpfContextMenuWithEnum:MainWindowModel>
</Window.DataContext>

<Window.Resources>
<ObjectDataProvider x:Key="EnumChoiceProvider" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="wpfContextMenuWithEnum:EnumChoice"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

<converter:EnumToBooleanConverter x:Key="EnumToBooleanConverter"></converter:EnumToBooleanConverter>
<converter:MultiBind2ValueComparerConverter x:Key="MultiBind2ValueComparerConverter"></converter:MultiBind2ValueComparerConverter>
</Window.Resources>

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

<TextBox Text="Right click me">
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding Source={StaticResource EnumChoiceProvider}}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem IsCheckable="True" Header="{Binding Path=.}">
<MenuItem.IsChecked>
<MultiBinding Converter="{StaticResource MultiBind2ValueComparerConverter}">
<Binding Path="DataContext.ModelEnumChoice" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" />
<Binding Path="." Mode="OneWay"></Binding>
</MultiBinding>
</MenuItem.IsChecked>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Window>

枚举:

using System.ComponentModel;

namespace WpfContextMenuWithEnum
{
public enum EnumChoice
{
[Description("Default")]
ChoiceDefault = 0, // easier if the default have value = 0

[Description("<1>")]
Choice1 = 1,

[Description("<2>")]
Choice2 = 2,
}
}

转换器:

using System;
using System.Windows;
using System.Windows.Data;

namespace WpfContextMenuWithEnum.Converter
{
public class ConverterWrapperWithDependencyParameterConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty ParameterProperty = DependencyProperty.Register("Parameter",
typeof(object), typeof(ConverterWrapperWithDependencyParameterConverter));

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter != null)
{
throw new ArgumentException("The parameter should be set directly as a property not into the Binding object.");
}

return Converter.Convert(value, targetType, Parameter, culture);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter != null)
{
throw new ArgumentException("The parameter should be set directly as a property not into the Binding object.");
}

return Converter.ConvertBack(value, targetType, Parameter, culture);
}

public object Parameter
{
get { return GetValue(ParameterProperty); }
set { SetValue(ParameterProperty, value); }
}

public IValueConverter Converter { get; set; }
}
}





using System;
using System.Windows.Data;

namespace WpfContextMenuWithEnum.Converter
{
public class EnumToBooleanConverter : IValueConverter
{
// **********************************************************************
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(parameter);
}

// **********************************************************************
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}

// **********************************************************************
}

}




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace WpfContextMenuWithEnum.Converter
{
public class MultiBind2ValueComparerConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Length != 2)
{
throw new ArgumentException("Can compare only 2 values together fo equality");
}

return (values[0].Equals(values[1]));
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
// if ((bool)value == true)
throw new NotImplementedException();
}
}
}

试验 1:MultiBindConverter ConvertBack 无法工作,它会丢失信息。

<ContextMenu ItemsSource="{Binding Source={StaticResource EnumChoiceProvider}}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem IsCheckable="True" Header="{Binding Path=.}">
<MenuItem.IsChecked>
<MultiBinding Converter="{StaticResource MultiBind2ValueComparerConverter}">
<Binding Path="DataContext.ModelEnumChoice" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" />
<Binding Path="."></Binding>
</MultiBinding>
</MenuItem.IsChecked>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>

试验 2:我的 ConverterParameter 绑定(bind)根本不起作用。它从未收到任何值(value)

<ContextMenu ItemsSource="{Binding Source={StaticResource EnumChoiceProvider}}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem IsCheckable="True" Header="{Binding Path=.}">
<MenuItem.IsChecked>
<Binding Path="DataContext.ModelEnumChoice" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}">
<Binding.Converter>
<converter:ConverterWrapperWithDependencyParameterConverter Converter="{StaticResource EnumToBooleanConverter}"
Parameter="{Binding Path=.}"/>
</Binding.Converter>
</Binding>
</MenuItem.IsChecked>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>

试验 3:

使用模板和 SelectedItem 的列表框,但 UI 不如应有的标准(出现额外的框架)。

最佳答案

所以你希望能够

  • 将任何 Enum 绑定(bind)到 ContextMenu 并显示它的 Description 属性
  • 在选定的Enum前打勾,在任何给定时间只有一个可以“激活”
  • 将所选值存储在 ViewModel 中并在选择更改时执行一些逻辑

像下面这样的东西?

imgur


MainWindow.xaml

<Window x:Class="WpfApplication1.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:WpfApplication1.ViewModel"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow"
Height="300"
Width="250">

<!-- Set data context -->
<Window.DataContext>
<viewModel:MainViewModel />
</Window.DataContext>

<!-- Converters -->
<Window.Resources>
<local:EnumDescriptionConverter x:Key="EnumDescriptionConverter" />
<local:EnumCheckedConverter x:Key="EnumCheckedConverter" />
</Window.Resources>

<!-- Element -->
<TextBox Text="Right click me">
<!-- Context menu -->
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding EnumChoiceProvider}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<!-- Menu item header bound to enum converter -->
<!-- IsChecked bound to current selection -->
<!-- Toggle bound to a command, setting current selection -->
<MenuItem
IsCheckable="True"
Width="150"
Header="{Binding Path=., Converter={StaticResource EnumDescriptionConverter}}"
Command="{Binding DataContext.ToggleEnumChoiceCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
CommandParameter="{Binding}">
<MenuItem.IsChecked>
<MultiBinding Mode="OneWay"
NotifyOnSourceUpdated="True"
UpdateSourceTrigger="PropertyChanged"
Converter="{StaticResource EnumCheckedConverter}">
<Binding Path="DataContext.SelectedEnumChoice"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}" />
<Binding Path="."></Binding>
</MultiBinding>
</MenuItem.IsChecked>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Window>

MainViewModel.cs

namespace WpfApplication1.ViewModel
{
public class MainViewModel : ViewModelBase // where base implements INotifyPropertyChanged
{
private EnumChoice? _selectedEnumChoice;

public MainViewModel()
{
EnumChoiceProvider = new ObservableCollection<EnumChoice>
(Enum.GetValues(typeof(EnumChoice)).Cast<EnumChoice>());

ToggleEnumChoiceCommand = new RelayCommand<EnumChoice>
(arg => SelectedEnumChoice = arg);
}

// Selections
public ObservableCollection<EnumChoice> EnumChoiceProvider { get; set; }

// Current selection
public EnumChoice? SelectedEnumChoice
{
get
{
return _selectedEnumChoice;
}
set
{
_selectedEnumChoice = value != _selectedEnumChoice ? value : null;
RaisePropertyChanged();
}
}

// "Selection changed" command
public ICommand ToggleEnumChoiceCommand { get; private set; }
}
}

EnumChoice.cs

namespace WpfApplication1
{
public enum EnumChoice
{
[Description("Default")]
ChoiceDefault,
[Description("<1>")]
Choice1,
[Description("<2>")]
Choice2
}
}

EnumDescriptionConverter.cs

namespace WpfApplication1
{
// Extract enum description
public class EnumDescriptionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
MemberInfo[] memberInfos = value.GetType().GetMember(value.ToString());

if (memberInfos.Length > 0)
{
object[] attrs = memberInfos[0].GetCustomAttributes(typeof (DescriptionAttribute), false);
if (attrs.Length > 0)
return ((DescriptionAttribute) attrs[0]).Description;
}

return value;

// or maybe just
//throw new InvalidEnumArgumentException(string.Format("no description found for enum {0}", value));
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

EnumCheckedConverter.cs

namespace WpfApplication1
{
// Check if currently selected
public class EnumCheckedConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return !values.Contains(null) && values[0].ToString().Equals(values[1].ToString(), StringComparison.OrdinalIgnoreCase);
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

关于c# - 从枚举值列表创建可检查上下文菜单的通用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32256222/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com