Как скрыть текстовое поле и всплывающее окно при выборе элемента из listview?

#c# #wpf

#c# #wpf

Вопрос:

У меня есть приложение WPF, в котором есть определенные элементы listview.Как и на изображении, есть 3 элемента: A, B, C. изначально, когда пользователь не выбирает ни одного элемента, текстовое поле будет скрыто.

Элементы ListView, когда текстовое поле скрыто

Элементы ListView, когда текстовое поле скрыто

Когда пользователь нажимает на любой элемент, я хочу, чтобы отображалось текстовое поле с описанием этого элемента.

Текстовое поле отображается при выборе элементов из listview

Текстовое поле отображается при выборе элементов из listview

Комментарии:

1. Смотрите Привязку к коллекциям , раздел Мастер-Подробный сценарий привязки .

Ответ №1:

Попробуйте это в коде (MainWindow.xaml.cs):

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApp7
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private MyViewModel m_MyViewModel;
        public MainWindow()
        {

            InitializeComponent();
            m_MyViewModel = new MyViewModel();
            myGrid.DataContext = MyVM;
        }

        public MyViewModel MyVM
        {
            get
            {
                return m_MyViewModel;
            }
        }
    }

    public class MyViewModel : ViewModelBase
    {
        public List<string> MyCollection
        {
            get
            {
                return new List<string> { "A", "B", "C" };
            }
        }

        private bool isListViewItemSelected;

        public bool IsListViewItemSelected
        {
            get
            {
                return isListViewItemSelected;
            }
            set
            {
                isListViewItemSelected = value;
                RaisePropertyChanged("IsListViewItemSelected");
            }
        }


        private string selectedItem;

        public string SelectedItem
        {
            get { return selectedItem; }
            set
            {
                if (value != selectedItem)
                {
                    selectedItem = value;
                    if (selectedItem == null)
                    {
                        IsListViewItemSelected = false;
                    }
                    else
                    {
                        IsListViewItemSelected = true;
                    }
                    RaisePropertyChanged("SelectedItem");
                    RaisePropertyChanged("SelectedTxtString");
                }
            }
        }

        public string SelectedTxtString
        {
            get
            {
                //return SelectedItem;
                return """   SelectedItem   "" is selected!";
            }
        }
    }

    public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
    {

        #region DisplayName

        /// <summary>
        /// Returns the user-friendly name of this object.
        /// Child classes can set this property to a new value,
        /// or override it to determine the value on-demand.
        /// </summary>
        public virtual string DisplayName { get; protected set; }

        #endregion // DisplayName

        #region Debugging Aides

        /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This 
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {
            // Verify that the property name matches a real,  
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: "   propertyName;

                if (this.ThrowOnInvalidPropertyName)
                    throw new Exception(msg);
                else
                    Debug.Fail(msg);
            }
        }

        /// <summary>
        /// Returns whether an exception is thrown, or if a Debug.Fail() is used
        /// when an invalid property name is passed to the VerifyPropertyName method.
        /// The default value is false, but subclasses used by unit tests might 
        /// override this property's getter to return true.
        /// </summary>
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

        #endregion // Debugging Aides

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
        protected virtual void RaisePropertyChanged(string propertyName)
        {
            VerifyPropertyName(propertyName);

            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion // INotifyPropertyChanged Members


        #region IDisposable Members

        /// <summary>
        /// Invoked when this object is being removed from the application
        /// and will be subject to garbage collection.
        /// </summary>
        public void Dispose()
        {
            this.OnDispose();
        }

        /// <summary>
        /// Child classes can override this method to perform 
        /// clean-up logic, such as removing event handlers.
        /// </summary>
        protected virtual void OnDispose()
        {
        }

        #endregion // IDisposable Members

    }
}
  

И это в MainWindow.xaml

 <Window x:Class="WpfApp7.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:local="clr-namespace:WpfApp7"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>

<Grid x:Name="myGrid">
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <ListView ItemsSource="{Binding MyCollection, Mode=OneWay}" SelectedItem="{Binding SelectedItem}">

    </ListView>

    <TextBox Grid.Row="1" Text="{Binding SelectedTxtString, Mode=OneWay}" 
             Visibility="{Binding IsListViewItemSelected, Converter={StaticResource BooleanToVisibilityConverter}}"/>

</Grid>
  

Комментарии:

1. Обратите внимание, что для вашего IsListViewItemSelected значения всегда установлено значение true , независимо от того, SelectedItem равно null или нет. Кроме того, установка UpdateSourceTrigger=PropertyChanged односторонней привязки (например, привязки текста и видимости текстового поля в вашем примере) бессмысленна. UpdateSourceTrigger влияет только на привязки TwoWay к OneWayToSource и не имеет ничего общего с событием PropertyChanged.

2. Также бессмысленно самостоятельно писать BoolToVisibilityConverter. В фреймворке уже есть одно.

3. Я написал это так, потому что в вопросе не было отмены выбора всех задействованных элементов ListViewItems… Но вы, конечно, правы. Должна быть проверка на то, что SelectedItem равен null…

4. О, я не знал, что в .NET есть реализация этого конвертера… Спасибо за совет!

5. И спасибо за комментарии по поводу UpdateSourceTrigger! Они действительно помогают мне понять процесс привязки!

Ответ №2:

Используйте привязку данных, чтобы привязать флаг видимости текстового поля к текущему выбранному элементу ListView.

В вашем контроллере или ViewModel реализуйте свойство для выбранного элемента

 public object SelectedItem { get; set; }
  

Привяжите его в ListView к свойству SelectedItem

 <ListView
    ...
    SelectedItem={Bining Path=SelectedItem}/>
  

Используйте второе свойство, чтобы определить, выбран ли элемент ListView

 public Visibility TextBoxVisibility=> SelectedItem != null 
    ? System.Windows.Visibility.Visible 
    : System.Windows.Visibility.Hidden;
  

В вашем xaml привяжите свойство видимости к свойству IsSelected

 <TextBox
    ...
    Visibility={Bining Path=TextBoxVisibility}/>
  

надеюсь, это поможет.

Комментарии:

1. Не забудьте отправить уведомление об изменении свойства для свойств SelectedItem модели представления и TextBoxVisibility. Также подумайте о том, чтобы вообще не использовать дополнительное свойство, а просто привязать видимость текстового поля также к SelectedItem с помощью конвертера привязки или, альтернативно, иметь стиль с DataTrigger для SelectedItem, который задает видимость.

2. да, верно. Внутри свойства, конечно, должно быть OnPropertyChanged(nameof(SelectedItem)), иначе пользовательский интерфейс не будет знать, когда изменилось значение

3. Не только OnPropertyChanged(nameof(SelectedItem)) но также OnPropertyChanged(nameof(TextBoxVisibility)) , потому что видимость TextBoxVisibility должна быть переоценена при изменении SelectedItem.

4. Я могу показать описание в текстовом поле, но при этом всегда остается место для текстового поля. Текстовое поле не скрыто, скрыто только содержимое внутри поля. Я также хочу скрыть текстовое поле, и оно должно быть видно только тогда, когда пользователь нажимает на элемент. Не могли бы вы, пожалуйста, помочь с этим.