2

enter image description here

I want to be able to select an item and then edit its label:

  • Select an item
  • Item is highlighted
  • Click on it's label
  • Label's TextBlock is replaced with TextBox
  • Modify the label
  • Only one item can be edited at a time

End the editing:

  1. Click onto item's icon:
    • TextBox is replaced back with TextBlock
    • Item remains highlighted
  2. Another item is clicked:
    • TextBox is replaced back with TextBlock
    • Edited item is unselected
    • Clicked item is selected and highlighted
  3. Any other area of the window is clicked:
    • TextBox is replaced back with TextBlock
    • Edited item remains highlighted

The behaviour should be pretty much as in Windows Explorer.

I managed to accomplish most of the requirements. Still I get random results. For example on first launch I could click straight onto label to edit it. The item itself remains not highlighted. This occurs only at start.

Also using the scroll-bar does not take focus off the list item. This allows to edit multiple items at the same time.

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>

Code

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; }
    }
}

Thanks for reading the long post.

I have searched and read many answers here in StackOverflow but none of them were great for me.

For example:

4

2 回答 2

0

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

于 2013-01-29T18:46:31.393 回答
0

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

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

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

查看此 stackoverflow 页面以将焦点从元素 WPF 中移开:如何以编程方式从 TextBox 中移除焦点

于 2013-01-29T17:50:02.453 回答