绑定的问题在于您试图绑定到一个对象。这在一个完美的OneWay
/OneTime
设想。但使用绑定时则不然TwoWay
。您可以更改属性的值,例如在您的视图模型中,但您无法更改对象实例本身。在您的具体情况下,绑定必须发送新的long
输入到视图模型的值集合并替换旧值。当然,这永远不会发生,因为Binding
并非旨在以这种方式工作。
技术原因是改变实例就意味着改变Binding.Source
。一旦绑定处于活动状态(由BindingExpression
)它变得不可变。不允许更改源。这也是原因{Binding Source={DynamicResource ...}}
行不通的。这BindingSource
只能是静态的(或StaticResource
- 不改变资源)。
您通常绑定到属性。在一个TwoWay
绑定场景Binding
可以简单地更新属性的值。所以解决你的问题的方法就是包装long
值到一个类中并绑定TextBox
到此类的属性来检索/修改实际值。
在这种情况下,您的代码看起来太复杂了。
您的对象结构太复杂或不自然。
您无需申请DataTemplate
to a ContentControl
(在 XAML 中)。
当然,由于这是一个 UWP 应用程序,因此请使用x:Bind
在可能的情况下,因为这会提高性能。转换器是冗余的,因为Binding
and x:Bind
允许嵌套PropertyPath
e.g.
<ListView ItemsSource="{Binding CurrentParameterValue.ListParameterValues}">
ItemsControl.ItemsSource
不需要TwoWay
捆绑。这ItemsControl
永远不会更新/替换源集合。如果您不打算替换视图模型中的源集合(例如,AtlasMethodParameterList = new ObservableCollection<>()
),那么你甚至可以将绑定模式设置为OneTime
(这将是默认的x:Bind
).
我建议使用OneTime
如果您需要替换集合,请致电Clear()
在集合上并添加新项目。这将提高性能。
Never use async void
在方法签名中,事件处理程序除外。
总是使用async Task
,当返回类型为void
或者当返回一个值时async Task<TResult>
。否则你会遇到意想不到的副作用,尤其是遇到异常时:
// An async void method must return Task
private async Task GetParameterList(string obj)
Also async
方法应该always敬请期待。这意味着该方法调用并等待async
方法本身必须返回Task
or Task<T>
值得期待。返回类型的方法void
不能等待。
All DependencyProperty
每个控件都有自己的Binding.UpdateSourceTrigger
set to UpdateSourceTrigger.PropertyChanged
默认情况下。
例外是可能引发过多连续属性更改的属性,例如TextBox
每次输入/按键都会执行。TextBox.Text
默认设置为UpdateSourceTrigger.LostFocus
.
你应该删除所有多余的UpdateSourceTrigger.PropertyChanged
从绑定中提高可读性。
考虑使用out
代替ref
如果您不打算读取该变量。如果你只设置值更喜欢使用out
向任何读者暗示您的意图。使用in
如果不想修改参考(只读参考)。
Your Set
方法应该看起来像这样:
protected virtual void Set<TValue>(out TValue valueTarget, TValue value, [CallerMemberName] string propertyName = null)
{
if (value != valueTarget)
{
valueTarget = value;
OnPropertyChanged(propertyName);
}
}
我重构了您的完整代码,试图改进它:
参数.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();
}
}
主页.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>