如果有人能在我发疯之前帮助我。我有一个包含ListBox的用户控件,我想为UserControl的SelectedItem添加一个属性,这样父控件就可以获得它。所以我使用了DependencyProperty
用户控件(版本列表.xaml):
<UserControl
x:Class="PcVueLauncher.Controls.VersionsList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:PcVueLauncher.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PcVueLauncher.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:Background="white"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<FrameworkElement.Resources>
<ResourceDictionary>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</ResourceDictionary>
</FrameworkElement.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Padding="10"
Text="Versions" />
<ListBox
Grid.Row="1"
d:ItemsSource="{d:SampleData ItemCount=5}"
ItemsSource="{Binding Versions}"
SelectedItem="{Binding SelectedVersion}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="5,5,10,5"
Text="{Binding VersionName}" />
<Button
Grid.Column="1"
Padding="5"
Command="{Binding RemoveVersionCommand}"
Content="Remove"
Visibility="{Binding CanBeRemoved, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
使用者控件相关的检视模型(版本清单检视模型)
namespace PcVueLauncher.ViewModels.Controls
{
public class VersionsListViewModel : ViewModelBase
{
private List<VersionPcVue> _versions;
public List<VersionPcVue> Versions
{
get
{
return _versions;
}
set
{
_versions = value;
OnPropertyChanged(nameof(Versions));
}
}
private VersionPcVue _selectedVersion;
public VersionPcVue SelectedVersion
{
get
{
return _selectedVersion;
}
set
{
_selectedVersion = value;
OnPropertyChanged(nameof(SelectedVersion));
}
}
public ICommand RemoveVersionCommand { get; }
public VersionsListViewModel()
{
List<VersionPcVue> versionPcVues = new()
{
new VersionPcVue{VersionName="V15"},
new VersionPcVue{VersionName="V12"}
};
Versions = versionPcVues;
}
}
}
后面的使用者控件程式码(VersionList.cs):
public partial class VersionsList : UserControl
{
public VersionsList()
{
InitializeComponent();
}
public VersionPcVue SelectedVersion
{
get { return (VersionPcVue)GetValue(SelectedVersionProperty); }
set { SetValue(SelectedVersionProperty, value); }
}
//Using a DependencyProperty as the backing store for SelectedVersion.This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedVersionProperty =
DependencyProperty.Register("SelectedVersion",
typeof(VersionPcVue),
typeof(VersionsList),
new FrameworkPropertyMetadata(
defaultValue: null,
flags: FrameworkPropertyMetadataOptions.AffectsMeasure,
propertyChangedCallback: new PropertyChangedCallback(OnSelectionChanged)));
private static void OnSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(SelectedVersionProperty);
}
}
// Register a dependency property with the specified property name,
// property type, owner type, property metadata, and callbacks.
public static readonly DependencyProperty SelectedVersionProperty = DependencyProperty.Register(
name: "SelectedVersion",
propertyType: typeof(VersionPcVue),
ownerType: typeof(VersionsList),
typeMetadata: new FrameworkPropertyMetadata(
defaultValue: null,
flags: FrameworkPropertyMetadataOptions.AffectsMeasure,
propertyChangedCallback: new PropertyChangedCallback(OnSelectionChanged)
));
private static void OnSelectionChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
depObj.CoerceValue(SelectedVersionProperty);
}
在包含UserControl的HomeView中,我有以下内容:
<UserControl
x:Class="PcVueLauncher.Views.HomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:PcVueLauncher.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PcVueLauncher.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=netstandard"
xmlns:viewmodels="clr-namespace:PcVueLauncher.ViewModels"
d:Background="White"
d:DataContext="{d:DesignInstance Type=viewmodels:HomeViewModel}"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Controls:VersionsList
x:Name="test"
Grid.Column="0"
DataContext="{Binding VersionsListViewModel}"
SelectedVersion="{Binding DataContext.SelectedVersion, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</UserControl>
并在关联的视图模型(HomeViewModel)
public class HomeViewModel : ViewModelBase
{
private IProjectService _projectService;
private VersionPcVue _selectedVersion;
public VersionPcVue SelectedVersion
{
get
{
return _selectedVersion;
}
set
{
_selectedVersion = value;
OnPropertyChanged(nameof(SelectedVersion));
}
}
private VersionPcVue _test1;
public VersionPcVue Test1
{
get
{
return _test1;
}
set
{
_test1 = value;
OnPropertyChanged(nameof(Test1));
}
}
private string _test;
public string Test
{
get
{
return _test;
}
set
{
_test = value;
OnPropertyChanged(nameof(Test));
}
}
private VersionsListViewModel versionsListViewModel;
public VersionsListViewModel VersionsListViewModel
{
get
{
return versionsListViewModel;
}
set
{
versionsListViewModel = value;
OnPropertyChanged(nameof(VersionsListViewModel));
}
}
public HomeViewModel(IProjectService projectService)
{
_projectService = projectService;
VersionsListViewModel = new();
}
}
当我从使用者控件变更选取的项目时,HomeViewModel中没有任何React。我想到系结错误,但为了尝试,我变更了这个
SelectedVersion="{Binding DataContext.SelectedVersionnnnnnn, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Grid}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Visual Studio告诉我,HomeViewModel中不存在SelectedVersionnnnn。
为什么无法将选定项恢复为HomeViewModel的SelectedVersion属性。
多谢你的帮忙
2条答案
按热度按时间0vvn1miw1#
您需要解决以下几个问题:
1.不要从属性更改回调中显式调用
DependencyObject.CoerceValue
。它由依赖属性系统自动调用-在属性更改回调之前。1.您的属性不会影响配置。为了获得较佳的效能,请不要设定
FrameworkPropertyMetadataOptions.AffectsMeasure
旗标,因为它会在每次属性变更时胁迫执行完整的配置传递,而在您的情况下,这是不必要的。ListBox.SelectedItem
不会影响控件的配置。您应该考虑设定FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
旗标,将属性设定为预设双向系结。Control
的内部绑定到控件的属性,而不是绑定到DataContext
。否则,您的控件将不便于处理(和编写)。例如,如果您更改DataContext
或重命名DataContext
返回的对象的属性,则必须重写内部绑定以处理新的对象结构/属性名称。B)这意味着您必须删除所有内部
DataContext
绑定,并为每个绑定引入一个依赖属性。例如,删除ListBox.ItemsSource
到VersionsListViewModel.Versions
属性的绑定,并引入一个依赖属性,如VersionsItemsSource
。1.例如,在
HomeView
中:定义与UserControl
的实际DataContext
相关的所有DataContext
相关Binding
(或一般为FrameworkElement
)而不是开始遍历(使用Binding.RelativeSource
)来寻找与系结目的的DataContext
相同的父系DataContext
。这是毫无意义的,只表明你还没有理解绑定是如何工作的。修复
1、2和3b
版本列表. xaml.cs
3a
版本列表.xaml
撰写
Control
时,一定要系结至控件的属性,而不是DataContext
的属性:4个
主页视图.xaml
请注意,
VersionsList
控件的DataContext
正在引用VersionsListViewModel
示例。必须相应地调整所有绑定:备注
“为什么我无法将选定项恢复到HomeViewModel得SelectedVersion属性.”
根据当前的类设计和控件配置,
VersionsList
控件绑定到VersionsListViewModel.SelectedVersion
属性。此时,您并不清楚真正想要什么。通过让
HomeViewModel
侦听VersionsListViewModel.SelectedVersion
属性更改来手动委托该值,或者从VersionsListViewModel
删除相关属性并直接绑定到HomeViewModel.SelectedVersion
。每个视图/控件的视图模型类在大多数情况下将导致错误的类设计/代码。应基于不同的考虑因素(如责任)创建单独的类。然后,您总是希望避免重复代码(如属性和逻辑):而不是将代码复制到单独的类中。
ryevplcw2#
在
VersionList.xaml
中:这只会将
ListBox.SelectedItem
绑定到{DataContext}.SelectedVersion
。然后,当选择一个项时,依赖属性VersionList.SelectedVersion
不会更新。方案一:按视图模型(无依赖关系属性)
我想你搞错了,因为你试图使用一个复杂的依赖属性。一个简单的方法是直接使用视图模型,而不使用依赖属性。
在
VersionsList.cs
中,移除SelectedVersionProperty
和SelectedVersion
成员。保持
VersionList.xaml
与:因此,
ListBox.SelectedItem
绑定到ListBox.DataContext.SelectedVersion
。如果ListBox.DataContext
是VersionsListViewModel
,则ListBox.SelectedItem
绑定到VersionsListViewModel.SelectedVersion
。在父控件
HomeView
中,它只需要传递一个VersionsListViewModel
给VersionList.DataContext
:如果
HomeView.DataContext
是HomeViewModel
,那么HomeView.VersionsList.ListBox.SelectedItem
是HomeViewModel.VersionsListViewModel.SelectedVersion
的绑定。最后,您可以移除成员
HomeViewModel.SelectedVersion
并使用HomeViewModel.VersionsListViewModel.SelectedVersion
。如果要保留成员
HomeViewModel.SelectedVersion
,则需要将HomeViewModel.SelectedVersion
重定向到HomeViewModel.cs
中的HomeViewModel.VersionsListViewModel.SelectedVersion
:诀窍是当
HomeViewModel.VersionsListViewModel.SelectedVersion
发生更改时,同时通知HomeViewModel.SelectedVersion
也发生了更改。方案二:依相依性属性
总而言之,当选择一个项时,您希望在
VersionsList.SelectedVersion
中设置所选项,那么您只需要将ListBox.SelectedItem
绑定到VersionsList.SelectedVersion
。首先,在
VersionList.cs
中添加依赖属性SelectedVersion
:单位:
{RelativeSource TemplatedParent}
指示绑定引用模板指向的元素,此处为VersionsList
。若要使用控件:
还更改了版本以协调绑定策略。
最后,您可以删除成员
VersionsListViewModel.SelectedVersion
(或者使用下面的技巧)。选择什么呢?
使用依赖属性,控件不会链接到视图模型类。我将使用这个来开发一个库,以在许多应用程序中重用。
在视图模型中,控件期望数据上下文中有特定的成员。我将在应用程序解决方案中使用这一点。