首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从ContextMenu实现"Rename“

从ContextMenu实现"Rename“
EN

Stack Overflow用户
提问于 2012-03-29 22:08:53
回答 3查看 2.7K关注 0票数 1

在WPF中从上下文菜单实现重命名功能的最佳方式是什么?我想要的是像Windows资源管理器这样的功能,在该功能中,您可以右键单击一个项目,获得一个上下文菜单,如果您选择重命名,文本将变为可编辑。

到目前为止,我已经尝试了以下方法,但我认为一定有更好的方法。这个解决方案的几乎所有方面都可以更改:)

代码语言:javascript
复制
<ListBox Grid.Row="0" MinWidth="200" MinHeight="75" ItemsSource="{Binding Path=DataSetsVM.DataSets, Source={StaticResource vmLocator}}" x:Name="dataSets">
<ListBox.ItemTemplate>
    <DataTemplate>
        <StackPanel x:Name="nameRoot">
            <TextBlock x:Name="nameBlock" Text="{Binding Path=Name}" HorizontalAlignment="Stretch" MinWidth="200" Tag="{Binding DataContext, ElementName=dataSets}">
            <TextBlock.ContextMenu> 
                <ContextMenu >
                    <ContextMenu.Resources>
                        <Style TargetType="Image">
                            <Setter Property="Height" Value="24"/>
                            <Setter Property="Width" Value="24"/>
                        </Style>
                    </ContextMenu.Resources>
                    <MenuItem Header="Rename" x:Name="RenameDatasetContext"
                              Command="{Binding PlacementTarget.Tag.CoreCommands.RenameDatasetCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
                              CommandParameter="{Binding PlacementTarget.DataContext, 
                              RelativeSource={RelativeSource FindAncestor, 
                              AncestorType={x:Type ContextMenu}}}">
                        <MenuItem.Icon>
                            <Image Source="{StaticResource RenameLargeIcon}"/>
                        </MenuItem.Icon>
                    </MenuItem>
                </ContextMenu>
            </TextBlock.ContextMenu>
            </TextBlock>
            <TextBox x:Name="nameBox"  Text="{Binding Path=Name}" HorizontalAlignment="Stretch" MinWidth="200" Visibility="Collapsed" />
        </StackPanel>
        <DataTemplate.Triggers>                                            
            <DataTrigger Binding="{Binding Path=IsEditable}" Value="True">
                <Setter TargetName="nameBlock" Property="Visibility" Value="Collapsed"/>
                <Setter TargetName="nameBox" Property="Visibility" Value="Visible"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
</ListBox.ItemTemplate> 
</ListBox>

Name的setter完成以下操作:

代码语言:javascript
复制
set
{
    if (IsEditable)
    {
        IsEditable = false;   
        if (_name == value)
        {
            RaisePropertyChanged(NamePropertyName);
            return;
        }
        // Do some back end stuff
        if (back end stuff worked)
        {
            var oldValue = _name;
            _name = value;
            RaisePropertyChanged(NamePropertyName);
        }
        else
        {
            // Do something about the error in the back end
        }
    }   
}

该命令所做的就是将IsEditable设置为true。

除非您不实际更改名称,否则这是有效的,此时没有任何东西会将IsEditable设置回来,因为名称的设置器永远不会被调用,所以您可以在重命名模式下得到一堆项。

我可以通过触发器在VM上设置属性吗,或者有更好的方法来做到这一点?

EN

回答 3

Stack Overflow用户

发布于 2012-03-31 01:29:47

这将是更多MVVM-ish。我简化了绑定以保持示例的简短和容易,并且我只是将ViewModel连接到cs。文件在后面进行快速测试,全部编译/运行。

代码语言:javascript
复制
<Window x:Class="Sample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Height="350" Width="525" 
    x:Name="wnd">

<ListBox ItemsSource="{Binding ViewModel.Items, ElementName=wnd}"
         SelectedItem="{Binding ViewModel.SelectedItem, ElementName=wnd}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock x:Name="nameBlock" Text="{Binding Name}" Margin="4">
                    <TextBlock.ContextMenu> 
                        <ContextMenu>
                            <MenuItem Header="Rename" 
                                      Command="{Binding EditItemCommand}">
                            </MenuItem>
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
                <TextBox x:Name="nameBox" Text="{Binding Name}" Visibility="Collapsed" Margin="2,0"/>
            </StackPanel>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding IsEditable}" Value="True">
                    <Setter TargetName="nameBlock" Property="Visibility" Value="Collapsed"/>
                    <Setter TargetName="nameBox" Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

代码隐藏(注意,视图是完全被动的):

代码语言:javascript
复制
public partial class MainWindow : Window
{
    public ViewModel ViewModel { get; set; }

    public MainWindow()
    {
        ViewModel = new ViewModel();
        InitializeComponent();
    }
}

viewModel (包含所有操作逻辑):

代码语言:javascript
复制
public class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<SomeClass> _items;
    private SomeClass _selectedItem;

    public ViewModel()
    {
        SelectedItem = Items.First(); 
    }

    public ObservableCollection<SomeClass> Items
    {
        get
        {
            if (_items == null)
            {
                _items = new ObservableCollection<SomeClass>();
                for (int i = 0; i < 10; i++) _items.Add(new SomeClass(string.Format("name {0}", i)));
            }

            return _items;
        }
    }

    public SomeClass SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            if (_selectedItem == value)
                return;

            _selectedItem = value;

            foreach (var item in Items) item.IsEditable = false;

            RaisePropertyChanged("SelectedItem");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

模型(对其他模型完全无能为力,只知道如何设置自己的行为),属性完全没有任何逻辑:

代码语言:javascript
复制
public class SomeClass : INotifyPropertyChanged
{
    private string _name;
    private bool _isEditable;
    private DelegateCommand _editItemCmd;

    public SomeClass(string name) { _name = name;}

    public DelegateCommand EditItemCommand
    {
        get
        {
            return _editItemCmd ?? (_editItemCmd = new DelegateCommand(() => { IsEditable = true; }));
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
                return;
            _name = value;
            RaisePropertyChanged("Name");
        }
    }

    public bool IsEditable
    {
        get { return _isEditable; }
        set
        {
            if(_isEditable == value)
                return;
            _isEditable = value;
            RaisePropertyChanged("IsEditable");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
票数 4
EN

Stack Overflow用户

发布于 2012-03-30 02:26:32

这很奇怪,但考虑到你的代码(它的编写方式),你处于一种古怪的情况下,所以最简单/最快的解决方案是在开始编辑新的ListBox项之前重置所有的all项:

在添加到nameBlock mousedown事件的XAML中:

代码语言:javascript
复制
<TextBlock x:Name="nameBlock" MouseDown="nameBlock_MouseDown"...

cs代码:

代码语言:javascript
复制
private object current;

private void nameBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (current != null && current == sender)
    return;
    foreach (var item in dataSets.ItemsSource)
        ((SomeClass)item).IsEditable = false;
}

还从设置器中删除了IsEditable检查

代码语言:javascript
复制
set
        {
            //if (IsEditable)
            {
                //IsEditable = false;
                if (_name == value)
                {
                    RaisePropertyChanged("Name");
                    return;
                }
                // Do some back end stuff
                if (back //end stuff //worked)
                {
                    var oldValue = _name;
                    _name = value;
                    RaisePropertyChanged("Name");
                }
            }
票数 1
EN

Stack Overflow用户

发布于 2012-03-30 05:58:16

有几件事。

首先,如果你想让它像Windows资源管理器一样工作,那么你应该在Esc中添加一个设置IsEditable=false的KeyBinding。

其次,您需要在LostFocus的TextBox上添加一个设置IsEditable=false的触发器。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9927092

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档