如何将当前列表项传递给 x:Bind 函数?
How do I pass the current list item to an x:Bind function?
我有一个简单的 ListView
绑定到对象列表。在 ListView.ItemTemplate
中,我想格式化每个对象并使用 x:Bind 函数绑定将其绑定到 TextBlock
,如下所示:<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(???)}" />
。但是,我不确定如何将对当前项的引用传递给函数。
如果我需要将项目的 属性 或什至多个属性(例如 <TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(Name, Model)}" />
)传递给函数,这可以正常工作,但我不知道如何传递整个对象。
我已经尝试了所有这些都无济于事:
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(this)}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(self)}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget({x:Bind})}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget({Binding})}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(Item)}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(DataContext)}" />
<TextBlock Name="Root" Text="{x:Bind local:MainViewModel.FormatWidget(Root.DataContext)}" />
作为解决方法,我可以在 returns 本身的数据项上创建并绑定到 属性,如下所示:
public Widget MeMyselfAndI { get => this; }
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(MeMyselfAndI)}" />
然而,这是一个 hack,在我的真实项目中我无法修改数据项 class 的源以添加 属性.
如何将对当前项的引用传递给函数?
这是一个重现:
MainPage.xaml
<Page
x:Class="ListItemFunctionBinding.MainPage"
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:local="using:ListItemFunctionBinding"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Widgets}" SelectedItem="{x:Bind ViewModel.SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Widget">
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(MeMyselfAndI)}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
MainPage.xaml.cs
using Windows.UI.Xaml.Controls;
namespace ListItemFunctionBinding
{
public sealed partial class MainPage : Page
{
public MainViewModel ViewModel { get; } = new MainViewModel();
public MainPage()
{
InitializeComponent();
}
}
}
MainViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace ListItemFunctionBinding
{
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Widget _selectedItem;
public Widget SelectedItem
{
get => _selectedItem;
set
{
if (_selectedItem != value)
{
_selectedItem = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}
}
public ObservableCollection<Widget> Widgets { get; } = new ObservableCollection<Widget>()
{
new Widget
{
Id = Guid.NewGuid(),
Name = "Regular Widget",
Model = "WX2020-01",
Description = "Your typical everyday widget."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Super Widget",
Model = "WX2020-02",
Description = "An extra special upgraded widget."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Broken Widget",
Model = "WX2020-03",
Description = "A widget that has been used and abused."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Fake Widget",
Model = "WX2020-04",
Description = "It's not really a widget at all!"
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Surprise Widget",
Model = "WX2020-05",
Description = "What kind of widget will it be?"
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Invisible Widget",
Model = "WX2020-06",
Description = "Our most inexpensive widget."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Backwards Widget",
Model = "WX2020-07",
Description = "Really more of a tegdiw, come to think of it."
}
};
public static string FormatWidget(Widget widget)
{
if (widget == null)
return "No widget selected";
else
return $"{widget.Name} [{widget.Model}] {widget.Description}";
}
}
}
Widget.cs
using System;
using System.ComponentModel;
namespace ListItemFunctionBinding
{
public class Widget : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Guid _id;
private string _name;
private string _model;
private string _description;
public Widget MeMyselfAndI { get => this; }
public Guid Id
{
get => _id;
set
{
if (_id != value)
{
_id = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Id)));
}
}
}
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
}
public string Model
{
get => _model;
set
{
if (_model != value)
{
_model = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Model)));
}
}
}
public string Description
{
get => _description;
set
{
if (_description != value)
{
_description = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Description)));
}
}
}
}
}
在这种情况下,您可以使用 Converter 方法传递您当前的项目(例如 Widget),然后在 Converter 中调用 MainViewModel.FormatWidget 方法。
.xaml:
<Page.Resources>
<local:MyConverter x:Key="MyConverter"/>
</Page.Resources>
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Widgets}" SelectedItem="{x:Bind ViewModel.SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Widget">
<TextBlock Text="{x:Bind Converter={StaticResource MyConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
.cs:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var widget = value as Widget;
return MainViewModel.FormatWidget(widget);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
我找到了解决方案 here:
Pathless casting
The native bind parser doesn't provide a keyword to represent this
as a function parameter, but it does support pathless casting (for example, {x:Bind (x:String)}
), which can be used as a function parameter. Therefore, {x:Bind MethodName((namespace:TypeOfThis))}
is a valid way to perform what is conceptually equivalent to {x:Bind MethodName(this)}
.
所以在我原来的例子中,我可以使用<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget((local:Widget))}" />
来绑定当前列表项。
这非常有效!耶!
我有一个简单的 ListView
绑定到对象列表。在 ListView.ItemTemplate
中,我想格式化每个对象并使用 x:Bind 函数绑定将其绑定到 TextBlock
,如下所示:<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(???)}" />
。但是,我不确定如何将对当前项的引用传递给函数。
如果我需要将项目的 属性 或什至多个属性(例如 <TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(Name, Model)}" />
)传递给函数,这可以正常工作,但我不知道如何传递整个对象。
我已经尝试了所有这些都无济于事:
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(this)}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(self)}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget({x:Bind})}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget({Binding})}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(Item)}" />
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(DataContext)}" />
<TextBlock Name="Root" Text="{x:Bind local:MainViewModel.FormatWidget(Root.DataContext)}" />
作为解决方法,我可以在 returns 本身的数据项上创建并绑定到 属性,如下所示:
public Widget MeMyselfAndI { get => this; }
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(MeMyselfAndI)}" />
然而,这是一个 hack,在我的真实项目中我无法修改数据项 class 的源以添加 属性.
如何将对当前项的引用传递给函数?
这是一个重现:
MainPage.xaml
<Page
x:Class="ListItemFunctionBinding.MainPage"
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:local="using:ListItemFunctionBinding"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Widgets}" SelectedItem="{x:Bind ViewModel.SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Widget">
<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget(MeMyselfAndI)}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
MainPage.xaml.cs
using Windows.UI.Xaml.Controls;
namespace ListItemFunctionBinding
{
public sealed partial class MainPage : Page
{
public MainViewModel ViewModel { get; } = new MainViewModel();
public MainPage()
{
InitializeComponent();
}
}
}
MainViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace ListItemFunctionBinding
{
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Widget _selectedItem;
public Widget SelectedItem
{
get => _selectedItem;
set
{
if (_selectedItem != value)
{
_selectedItem = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}
}
public ObservableCollection<Widget> Widgets { get; } = new ObservableCollection<Widget>()
{
new Widget
{
Id = Guid.NewGuid(),
Name = "Regular Widget",
Model = "WX2020-01",
Description = "Your typical everyday widget."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Super Widget",
Model = "WX2020-02",
Description = "An extra special upgraded widget."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Broken Widget",
Model = "WX2020-03",
Description = "A widget that has been used and abused."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Fake Widget",
Model = "WX2020-04",
Description = "It's not really a widget at all!"
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Surprise Widget",
Model = "WX2020-05",
Description = "What kind of widget will it be?"
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Invisible Widget",
Model = "WX2020-06",
Description = "Our most inexpensive widget."
},
new Widget
{
Id = Guid.NewGuid(),
Name = "Backwards Widget",
Model = "WX2020-07",
Description = "Really more of a tegdiw, come to think of it."
}
};
public static string FormatWidget(Widget widget)
{
if (widget == null)
return "No widget selected";
else
return $"{widget.Name} [{widget.Model}] {widget.Description}";
}
}
}
Widget.cs
using System;
using System.ComponentModel;
namespace ListItemFunctionBinding
{
public class Widget : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Guid _id;
private string _name;
private string _model;
private string _description;
public Widget MeMyselfAndI { get => this; }
public Guid Id
{
get => _id;
set
{
if (_id != value)
{
_id = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Id)));
}
}
}
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
}
public string Model
{
get => _model;
set
{
if (_model != value)
{
_model = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Model)));
}
}
}
public string Description
{
get => _description;
set
{
if (_description != value)
{
_description = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Description)));
}
}
}
}
}
在这种情况下,您可以使用 Converter 方法传递您当前的项目(例如 Widget),然后在 Converter 中调用 MainViewModel.FormatWidget 方法。
.xaml:
<Page.Resources>
<local:MyConverter x:Key="MyConverter"/>
</Page.Resources>
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Widgets}" SelectedItem="{x:Bind ViewModel.SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Widget">
<TextBlock Text="{x:Bind Converter={StaticResource MyConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
.cs:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var widget = value as Widget;
return MainViewModel.FormatWidget(widget);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
我找到了解决方案 here:
Pathless casting
The native bind parser doesn't provide a keyword to represent
this
as a function parameter, but it does support pathless casting (for example,{x:Bind (x:String)}
), which can be used as a function parameter. Therefore,{x:Bind MethodName((namespace:TypeOfThis))}
is a valid way to perform what is conceptually equivalent to{x:Bind MethodName(this)}
.
所以在我原来的例子中,我可以使用<TextBlock Text="{x:Bind local:MainViewModel.FormatWidget((local:Widget))}" />
来绑定当前列表项。
这非常有效!耶!