c# - ListView 内的 TextBox 绑定(bind)到对象,双向绑定(bind)不起作用

标签 c# wpf xaml datatemplate

编辑:

好吧,在尝试了无数次但没有成功之后,我创建了一个非常小的 Wpf 应用程序。您可以直接复制此代码。请注意,当您更改文本框中的值并按“测试”按钮时,这些值永远不会更新。我不明白为什么双向绑定(bind)不起作用。请帮忙。

这是 xaml:

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListView Grid.Row="0" 
                 ItemsSource="{Binding Path=Demo.CurrentParameterValue,Mode=TwoWay}" 
                 HorizontalAlignment="Center" VerticalAlignment="Center">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=.,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Width="100"></TextBox>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <Button Grid.Row="1" Click="Button_Click">TEST</Button>
    </Grid>

这是 xaml.cs:

namespace WpfApp9
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private VmServiceMethodsViewDataGridModel _demo;

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        public VmServiceMethodsViewDataGridModel Demo
        {
            get => _demo;
            set
            {
                _demo = value;
                OnPropertyChanged("Demo");
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            Demo = new VmServiceMethodsViewDataGridModel();
            Demo.CurrentParameterValue.Add(1);
            Demo.CurrentParameterValue.Add(2);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var collection = Demo.CurrentParameterValue;
            MessageBox.Show(string.Format("Values are {0}, {1}", collection[0], collection[1]));
        }
    }

    public class VmServiceMethodsViewDataGridModel : INotifyPropertyChanged
    {
        private List<object> _currentParameterValue;
        public List<object> CurrentParameterValue
        {
            get => _currentParameterValue;
            set
            {
                _currentParameterValue = value;
                OnPropertyChanged("CurrentParameterValue");
            }
        }

        public VmServiceMethodsViewDataGridModel()
        {
            CurrentParameterValue = new List<object>();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }

最佳答案

您的绑定(bind)问题在于您正在尝试绑定(bind)到一个对象。这在 OneWay 中完全没问题。/OneTime设想。但使用绑定(bind)时则不然 TwoWay 。您可以更改属性的值,例如在您的 View 模型中,但您无法更改对象实例本身。在您的具体情况下,绑定(bind)必须发送新的 long输入到 View 模型的值集合并替换旧值。当然,这永远不会发生,因为 Binding并非旨在以这种方式工作。
技术原因是更改实例意味着更改 Binding.Source 。一旦绑定(bind)处于事件状态(由 BindingExpression 控制),它就变得不可变。不允许更改源。这也是{Binding Source={DynamicResource ...}}的原因行不通的。 BindingSource只能是静态的(或 StaticResource - 不更改资源)。

您通常绑定(bind)到属性。在 TwoWay绑定(bind)场景Binding可以简单地更新属性的值。因此,解决您问题的方法是将 long 包装起来。值到一个类中并绑定(bind) TextBox到此类的属性来检索/修改实际值。

在这种情况下,您的代码看起来太复杂了。
您的对象结构太复杂或不自然。

您不需要应用 DataTemplateContentControl (在 XAML 中)。
当然,由于这是一个 UWP 应用程序,因此请使用 x:Bind在可能的情况下,因为这会提高性能。转换器是冗余的,如 Bindingx:Bind允许嵌套 PropertyPath例如

<ListView ItemsSource="{Binding CurrentParameterValue.ListParameterValues}">

ItemsControl.ItemsSource不需要TwoWay捆绑。 ItemsControl永远不会更新/替换源集合。如果您不打算替换 View 模型中的源集合(例如 AtlasMethodParameterList = new ObservableCollection<>() ),那么您甚至可以将绑定(bind)模式设置为 OneTime (这将是 x:Bind 的默认值)。
我建议使用OneTime如果您需要更换集合,请调用 Clear()在集合上并添加新项目。这将提高性能。

永远不要使用 async void在方法签名中,事件处理程序除外。
始终使用async Task ,当返回类型为 void 时或者返回值 async Task<TResult> 时。否则你会遇到意想不到的副作用,尤其是遇到异常时:

// An async void method must return Task
private async Task GetParameterList(string obj)

还有async方法应该始终等待。这意味着该方法调用并等待 async方法本身必须返回 TaskTask<T>值得期待。返回类型 void 的方法不能等待。

全部DependencyProperty每个控件都有其Binding.UpdateSourceTrigger设置为UpdateSourceTrigger.PropertyChanged默认情况下。
异常(exception)的是可能引发过多连续属性更改的属性,例如 TextBox每次输入/按键都会执行。 TextBox.Text默认设置为 UpdateSourceTrigger.LostFocus .
您应该删除所有多余的 UpdateSourceTrigger.PropertyChanged从绑定(bind)中提高可读性。

考虑使用out而不是ref如果您不打算读取该变量。如果只设置值更喜欢使用 out向任何读者暗示您的意图。使用in如果不想修改引用(只读引用)。
您的Set方法应该如下所示:

protected virtual void Set<TValue>(out TValue valueTarget, TValue value, [CallerMemberName] string propertyName = null)
{
  if (value != valueTarget)
  {
    valueTarget = value;
    OnPropertyChanged(propertyName);
  }
}

我重构了您的完整代码并试图改进它:

Parameter.cs

// The type that wraps the actual parameter value.
// Consider to use dedicated types e.g., LongParameter instead, to allow a strongly typed Value property instead of a basic property of type object.
// This prevents implicit boxing/unboxing in order to convert from object/reference type to primitive/value type and vice versa. This will improve performance. 
// (Only needed because we are dealing with primitive/value types like long, double, etc)
// You would then have to define a DataTemplate for each type. Don't forget to set x:DataType on each DataTemplate.
public class Parameter : BindableBase
{
  protected Parameter(object value)
  {
    this.Value = value;
  }

  private object value;
  public object Value
  {
    get => this.value;
    set => Set(out this.value, value);
  }
}

VmServiceModel.cs

public class VmServiceModel : BindableBase
{    
  public VmServiceModel()
  {
    this.Parameters = new List<Parameter>();
  }

  private List<Parameter> _parameters;
  public List<Parameter> Parameters
  {
    get => this._parameters;
    set => Set(out this._parameters, value);
  }
}

ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{
  public ViewModel()
  {
    this.AtlasMethodParameterList = new ObservableCollection<VmServiceModel>();
  }

  private ObservableCollection<VmServiceModel> _atlasMethodParameterList;
  public ObservableCollection<VmServiceModel> AtlasMethodParameterList
  {
    get => _atlasMethodParameterList;
    set => Set(out _atlasMethodParameterList, value);
  }

  private async Task GetParameterList(string obj)
  {
    foreach (var item in this.ParametersCollection)
    {
      var vmServiceModel = new VmServiceModel();
      vmServiceModel.Parameters
        .AddRange(item.Value.Cast<long>().Select(innerItem => new Parameter(innerItem)));

      this.AtlasMethodParameterList.Add(vmServiceModel);
    }
  }
}

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
  public ViewModel ViewModel { get; set; }

  public MainPage()
  {
    this.InitializeComponent();
    this.ViewModel = new ViewModel();
  }
}

MainPage.xaml

<Page>
  <Page.Resources>
    <DataTemplate x:Key="ListIntTemplate" x:DataType="local:VmServiceModel">
      <ListView ItemsSource="{x:Bind Parameters}"
                HorizontalAlignment="Center" 
                SelectionMode="None" Background="Transparent">
        <ListView.ItemsPanel>
          <ItemsPanelTemplate>
            <controls:WrapPanel VerticalAlignment="Top"/>
          </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
          <DataTemplate x:DataType="local:Parameter">
            <TextBox Text="{Binding Value Mode=TwoWay}" Height="36" Width="65"/>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
    </DataTemplate>
  </Page.Resources>

  <Grid>
    <ListView ItemsSource="{x:Bind ViewModel.AtlasMethodParameterList}" 
              ItemTemplate="{StaticResource ListIntTemplate}">
    </ListView>
  </Grid>
</Page>

关于c# - ListView 内的 TextBox 绑定(bind)到对象,双向绑定(bind)不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61965743/

相关文章:

c# - 禁用网络浏览器而不灰显

c# - 仅显示来自实体对象的 DataGrid 中的某些列

wpf - 在样式中分配 ValidatesOnDataErrors

c# - 如何延迟对列表中属性的调用?

c# - WPF 数据网格 : Save cell changes immediately with MVVM

c# - 检索电子邮件的 AlternateView

c# - Quartz.Net桌面应用实现/C#

wpf - DataGridCheckBoxColumn IsReadOnly 属性绑定(bind)

c# - 如何在项目MVVM Toolkit中的ViewModel之间共享信息?

c# - 使用 WriteableBitmap 的缓冲区大小不足?