在WPF中从上下文菜单实现重命名功能的最佳方式是什么?我想要的是像Windows资源管理器这样的功能,在该功能中,您可以右键单击一个项目,获得一个上下文菜单,如果您选择重命名,文本将变为可编辑。
到目前为止,我已经尝试了以下方法,但我认为一定有更好的方法。这个解决方案的几乎所有方面都可以更改:)
<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完成以下操作:
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上设置属性吗,或者有更好的方法来做到这一点?
发布于 2012-03-31 01:29:47
这将是更多MVVM-ish。我简化了绑定以保持示例的简短和容易,并且我只是将ViewModel连接到cs。文件在后面进行快速测试,全部编译/运行。
<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>
代码隐藏(注意,视图是完全被动的):
public partial class MainWindow : Window
{
public ViewModel ViewModel { get; set; }
public MainWindow()
{
ViewModel = new ViewModel();
InitializeComponent();
}
}viewModel (包含所有操作逻辑):
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;
}模型(对其他模型完全无能为力,只知道如何设置自己的行为),属性完全没有任何逻辑:
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;
}发布于 2012-03-30 02:26:32
这很奇怪,但考虑到你的代码(它的编写方式),你处于一种古怪的情况下,所以最简单/最快的解决方案是在开始编辑新的ListBox项之前重置所有的all项:
在添加到nameBlock mousedown事件的XAML中:
<TextBlock x:Name="nameBlock" MouseDown="nameBlock_MouseDown"...cs代码:
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检查
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");
}
}发布于 2012-03-30 05:58:16
有几件事。
首先,如果你想让它像Windows资源管理器一样工作,那么你应该在Esc中添加一个设置IsEditable=false的KeyBinding。
其次,您需要在LostFocus的TextBox上添加一个设置IsEditable=false的触发器。
https://stackoverflow.com/questions/9927092
复制相似问题