WPF:ListView 和编辑 ListViewItem

问题描述 投票:0回答:2

enter image description here

我希望能够选择一个项目,然后编辑其标签:

  • 选择一个项目
  • 项目突出显示
  • 点击它的标签
  • Label 的 TextBlock 替换为 TextBox
  • 修改标签
  • 一次只能编辑一项

结束编辑:

  1. 单击项目的图标:
    • TextBox 被替换回 TextBlock
    • 项目保持突出显示
  2. 单击另一个项目:
    • TextBox 被替换回 TextBlock
    • 编辑的项目未被选中
    • 单击的项目被选中并突出显示
  3. 单击窗口的任何其他区域:
    • TextBox 被替换回 TextBlock
    • 编辑的项目保持突出显示

该行为应该与 Windows 资源管理器中的行为非常相似。

我设法完成了大部分要求。我仍然得到随机结果。例如,在第一次启动时,我可以直接单击标签进行编辑。该项目本身仍然不突出显示。这仅在开始时发生。

此外,使用滚动条不会将焦点从列表项上移开。这允许同时编辑多个项目。

XAML

<Window x:Class="WPFComponents.DailyImages"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Model="clr-namespace:WPFComponents.Model"
    Title="Media Items" Height="300" Width="300">

<ListView x:Name="_mediaItemList" ItemsSource="{Binding MediaItems}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Multiple"
          MouseLeftButtonDown="OnClickMediaList" IsSynchronizedWithCurrentItem="True">

    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
        </Style>
    </ListView.ItemContainerStyle>


    <ListView.ItemTemplate>
        <DataTemplate DataType="Model:MediaItem">
            <Grid Width="80" Margin="4">
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Image HorizontalAlignment="Center" Stretch="Uniform" Source="{Binding Path=IconPath}" Width="70" />

                <StackPanel Grid.Row="2">
                    <TextBlock Text="{Binding Path=Date}" TextWrapping="Wrap" />

                    <TextBlock x:Name="_labelTextBlock" Text="{Binding Path=Label}" TextWrapping="Wrap"
                               PreviewMouseLeftButtonDown="OnClickLabelBlock">
                        <TextBlock.Style>
                            <Style TargetType="TextBlock">
                                <Setter Property="Visibility" Value="Visible" />
                            </Style>
                        </TextBlock.Style>
                    </TextBlock>

                    <TextBox x:Name="_labelTextBox" Text="{Binding Path=Label}" Visibility="Collapsed"
                             TextWrapping="WrapWithOverflow" TextAlignment="Center"
                             LostFocus="OnTextLostFocus">
                    </TextBox>
                </StackPanel>
            </Grid>

            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding IsEditing}" Value="True">
                    <Setter TargetName="_labelTextBlock" Property="Visibility" Value="Collapsed" />
                    <Setter TargetName="_labelTextBox" Property="Visibility" Value="Visible" />
                </DataTrigger>
            </DataTemplate.Triggers>

        </DataTemplate>
    </ListView.ItemTemplate>

    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel IsItemsHost="True" VerticalAlignment="Top" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>

</ListView>

代码

public partial class DailyImages
{
    public DailyImages()
    {
        InitializeComponent();

        ViewModel.DailyImages dailyImages = new ViewModel.DailyImages();
        _mediaItemList.DataContext = dailyImages;
    }

    private void OnClickLabelBlock(object sender, MouseButtonEventArgs e)
    {
        TextBlock notes = sender as TextBlock;
        if (notes == null) return;

        MediaItem selectedMedia = notes.DataContext as MediaItem;
        if (selectedMedia == null) return;

        // Multiple items might be selected
        // Clear all selected items
        _mediaItemList.SelectedItems.Clear();

        // Reselect
        selectedMedia.IsSelected = true;
        selectedMedia.IsEditing = true;

        Mouse.Capture(this, CaptureMode.SubTree);
    }

    private void OnTextLostFocus(object sender, RoutedEventArgs e)
    {
        TextBox textBox = sender as TextBox;
        if (textBox == null) return;

        MediaItem mediaItem = textBox.DataContext as MediaItem;
        if (mediaItem == null)
            return;

        // End the label editing
        mediaItem.IsEditing = false;

        ReleaseMouseCapture();
    }

    private void OnClickMediaList(object sender, MouseButtonEventArgs e)
    {
        // End the label editing
        foreach (MediaItem mediaItem in _mediaItemList.Items)
            mediaItem.IsEditing = false;

        ReleaseMouseCapture();
    }
}

MediaItem.cs

public class MediaItem : INotifyPropertyChanged
{
    private bool _isEditing;
    private bool _isSelected;
    private string _label;

    public MediaItem()
    {
        IsEditing = false;
        _isSelected = false;
    }

    public bool IsEditing
    {
        get { return _isEditing; }
        set
        {
            if (_isEditing == value) return;
            _isEditing = value;
            OnPropertyChanged("IsEditing");
        }
    }

    public string Label
    {
        get { return _label; }
        set
        {
            _label = value;
            OnPropertyChanged("Label");
        }
    }

    public DateTime Date { get; set; }
    public string IconPath { get; set; }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

DailyImages.cs

public class DailyImages
{
    private ObservableCollection<MediaItem> _mediaItems;

    public DailyImages()
    {
        _mediaItems = new ObservableCollection<MediaItem>();

        _mediaItems.Add(new MediaItem {Label = "Image 1", IconPath = "Resources/Icon1.png"});
        _mediaItems.Add(new MediaItem {Label = "Image 2", IconPath = "Resources/Icon2.png"});
        _mediaItems.Add(new MediaItem {Label = "Image 3", IconPath = "Resources/Icon3.png"});
        _mediaItems.Add(new MediaItem {Label = "Image 4", IconPath = "Resources/Icon4.png"});
        _mediaItems.Add(new MediaItem {Label = "Image 5", IconPath = "Resources/Icon5.jpg"});
        _mediaItems.Add(new MediaItem {Label = "Image 6", IconPath = "Resources/Icon6.png"});
        _mediaItems.Add(new MediaItem {Label = "Image 7", IconPath = "Resources/Icon7.png"});
        _mediaItems.Add(new MediaItem {Label = "Image 8", IconPath = "Resources/Icon8.png"});
        _mediaItems.Add(new MediaItem {Label = "Image 9", IconPath = "Resources/Icon9.png"});
    }

    public ObservableCollection<MediaItem> MediaItems
    {
        get { return _mediaItems; }
        set { _mediaItems = value; }
    }
}

感谢您阅读这么长的帖子。

我在 StackOverflow 中搜索并阅读了很多答案,但没有一个适合我。

例如:

c# wpf listview
2个回答
0
投票

您可能希望捕获列表视图上的滚动事件并显式删除元素上的焦点。

    ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(mTreeView);
    scrollViewer.ScrollChanged += new ScrollChangedEventHandler(scrollViewer.....

并在处理程序中删除焦点。

查看此 stackoverflow 页面,将焦点从元素上移开 WPF:如何以编程方式从文本框移除焦点


0
投票

也许在不同状态下切换 IsReadonly 属性会更好?然后您将不再需要切换可见性。另外,这种情况下最好更换模板。

© www.soinside.com 2019 - 2024. All rights reserved.