我有一个带有名称列表的ListView,我希望能够通过双击或按钮重命名每个值。
我已经为doubleclick做了这个,并且正在使用它:
WPF
<ListView Grid.Row="0" x:Name="ListProfileView"
ItemsSource="{Binding ProfilesCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}" IsReadOnly="True" VerticalAlignment="Center">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction
Command="{Binding DataContext.RenameCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<i:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource MultiConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type TextBox}}"/>
<Binding Source="{x:Static classes:BooleanHelper.False}"/>
</MultiBinding>
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction
Command="{Binding DataContext.RenameCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<i:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource MultiConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type TextBox}}"/>
<Binding Source="{x:Static classes:BooleanHelper.True}"/>
</MultiBinding>
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
c#(带有ICommand的MVVM模型):
private ICommand _renameCommand;
/// <summary>
/// Command used to change the name of the selected profile.
/// </summary>
public ICommand RenameCommand
{
get
{
return _renameCommand ?? (_renameCommand = new RelayCommand<object>(obj =>
{
if(!(obj is object[] values)) return;
if(!(values[0] is TextBox txtBox) || !(values[1] is bool value)) return;
txtBox.IsReadOnly = value;
if (!value)
{
txtBox.Focus();
}
}));
}
}
但对于按钮,我不知道如何获取文本框的路径以使用相同的命令。 我尝试过这样的事情:
<Button Grid.Column="3" Content="{x:Static dictionnaries:ColorConfigurationDictionnary.rename}"
FontWeight="SemiBold"
Command="{Binding RenameCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MultiConverter}">
<Binding ElementName="ListProfileView" Path="ItemContainerGenerator"/>
<Binding Source="{x:Static classes:BooleanHelper.False}"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
但我没有想法......这可能吗?
答案 0 :(得分:0)
似乎存在某种错误信息,所以让我描述一下MvvM如何以我能想到的最佳方式工作。
模型是存储数据的地方,所以我们称之为个人资料:
namespace Model
{
public class Profile
{
public string Name { get; set; }
}
}
现在你需要的是一个ViewModel,它将提供被操纵数据的信息:
using VM.Commands;
namespace VM
{
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
ProfilesCollection = new List<Profile>();
for (int i = 0; i < 100; i++)
{
ProfilesCollection.Add(new Profile() {Name = $"Name {i}"});
}
RenameCommand = new TestCommand(renameCommandMethod, (o) => true);
}
void renameCommandMethod(object parameter)// to manipulate the colleciton you use the Commands which you already do but without the need for converters or any UI elements. Makes it much easier to handle.
{
string renameTo = parameter.ToString();
foreach (var profile in ProfilesCollection)
{
profile.Name = renameTo;
}
}
private List<Profile> profilesCollection;
public List<Profile> ProfilesCollection
{
get { return profilesCollection; }
set { profilesCollection = value; OnPropertyChanged(); }
}
private ICommand renameCommand;
public ICommand RenameCommand
{
get { return renameCommand; }
set { renameCommand = value; }
}
RelayCommand
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace VM.Commands
{
public class TestCommand : ICommand
{
private Action<object> _execute;
private Predicate<object> _canExecute;
public TestCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
#region Implementation of ICommand
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke(parameter) ?? true;
}
public void Execute(object parameter)
{
_execute?.Invoke(parameter);
}
public event EventHandler CanExecuteChanged;
#endregion
}
}
然后UI看起来像这样:
<Window x:Class="SO_app.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:VM;assembly=VM"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:converter="clr-namespace:SO_app.Converters"
xmlns:validation="clr-namespace:SO_app.Validation"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:local="clr-namespace:SO_app"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:model="clr-namespace:Model;assembly=Model"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" Height="452.762" Width="525" Closing="Window_Closing">
<Window.Resources>
<CollectionViewSource Source="{Binding ProfilesCollection}" x:Key="profiles"/>
</Window.Resources>
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<Window.Background>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle Width="50" Height="50" Fill="ForestGreen"></Rectangle>
</VisualBrush.Visual>
</VisualBrush>
</Window.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Source={StaticResource profiles}}"
VirtualizingPanel.VirtualizationMode="Recycling">
<ListView.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<ToolTip x:Key="Tip">
<TextBlock>
<Run>Some text here</Run>
<LineBreak/>
<Run Text="{Binding Name, StringFormat='Actual Text: {0}'}"/>
</TextBlock>
</ToolTip>
</DataTemplate.Resources>
<TextBlock Text="{Binding Name}" ToolTip="{StaticResource Tip}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Column="1">
<Button Content="Rename" Command="{Binding RenameCommand}" CommandParameter="NewName"></Button>
</StackPanel>
</Grid>
答案 1 :(得分:0)
这是我做的: 这让我只通过禁用列表视图中文本框的只读来更改一个值的名称。 我在后面的GUI代码中写道。
private ICommand _renameCommand;
/// <summary>
/// Command used to change the name of the selected profile.
/// </summary>
public ICommand RenameCommand
{
get
{
return _renameCommand ?? (_renameCommand = new RelayCommand<object>(obj =>
{
if(!(obj is object[] values)) return;
if(!(values[0] is TextBox || values[0] is SetConfiguration) || !(values[1] is bool value)) return;
if (values[0] is TextBox txtBox)
{
txtBox.IsReadOnly = value;
if (!value)
{
txtBox.Focus();
txtBox.SelectAll();
}
}
if (values[0] is SetConfiguration config)
{
var listView = ListProfileView.ItemContainerGenerator.ContainerFromItem(config) as ListViewItem;
var presenter = FindVisualChild<ContentPresenter>(listView);
if(!(presenter.ContentTemplate.FindName("ProfileName", presenter) is TextBox txtBoxItem)) return;
if (!value)
{
txtBoxItem.Focus();
txtBoxItem.SelectAll();
}
txtBoxItem.IsReadOnly = value;
}
}));
}
}
private static TChildItem FindVisualChild<TChildItem>(DependencyObject obj)
where TChildItem : DependencyObject
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is TChildItem item)
return item;
var childOfChild = FindVisualChild<TChildItem>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}