【发布时间】:2019-03-25 11:24:30
【问题描述】:
我有一个 WPF 应用程序,其中有某些列表视图项。如图中有 3 个项目,A、B、C。最初,当用户不选择任何项目时,文本框将被隐藏。
TextBox 隐藏时的ListView Items
当用户单击任何项目时,我希望显示一个包含该项目描述的文本框。
当列表视图中的项目被选中时,文本框显示
【问题讨论】:
-
参见Binding to Collections,主从绑定方案部分。
我有一个 WPF 应用程序,其中有某些列表视图项。如图中有 3 个项目,A、B、C。最初,当用户不选择任何项目时,文本框将被隐藏。
TextBox 隐藏时的ListView Items
当用户单击任何项目时,我希望显示一个包含该项目描述的文本框。
当列表视图中的项目被选中时,文本框显示
【问题讨论】:
在后面的代码中试试这个(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>
【讨论】:
SelectedItem 是否为空,您的IsListViewItemSelected 始终设置为true。除此之外,在 OneWay 绑定上设置 UpdateSourceTrigger=PropertyChanged(如示例中的 TextBox 的文本和可见性绑定)是没有意义的。 UpdateSourceTrigger 仅在 OneWayToSource Bindings 的 TwoWay 中起作用,与 PropertyChanged 事件无关。
使用数据绑定将文本框的可见性标志绑定到当前选中的ListView Item。
在您的 Controller 或 ViewModel 中为 SelectedItem 实现一个属性
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 中将 Visibility 属性绑定到 IsSelected 属性
<TextBox
...
Visibility={Bining Path=TextBoxVisibility}/>
希望这会有所帮助。
【讨论】:
OnPropertyChanged(nameof(SelectedItem)),还有OnPropertyChanged(nameof(TextBoxVisibility)),因为当 SelectedItem 发生变化时必须重新评估 TextBoxVisibility。