gpt4 book ai didi

c# - 在 View 完成加载之前调用绑定(bind)属性的 setter

转载 作者:行者123 更新时间:2023-12-03 10:29:50 24 4
gpt4 key购买 nike

我目前正在 WPF(Surface 2.0)中开发,并在我的应用程序的大部分部分使用 MVVM 模式。不幸的是,我目前面临一个相当复杂的问题,希望你们能帮助我:

我有一个 View 和一个 ViewModel 属于它。 View 包含到 ViewModel 中属性的双向绑定(bind):

<pb:PivotBar ItemsSource="{Binding PivotBarEntries}" 
SelectedItemIndex="{Binding SelectedPivotItemIndex, Mode=TwoWay}" />

(...)

<local:SomeOtherView />

首次加载 View 时,将调用 SelectedPivotItemIndex 的 setter 。这很好,只是在加载 View 的其余部分之前调用了 setter。由于 setter 将消息(通过 MVVMLight 的 Messenger)发送到稍后在 View 中创建的其他 View 模型,这是一个问题 - 这些消息永远不会到达它们的目的地,因为到目前为止还没有为它们注册接收器。
public int SelectedPivotItemIndex
{
get
{
return this.selectedPivotItemIndex;
}
set
{
if (value != this.selectedPivotItemIndex)
{
this.selectedPivotItemIndex = value;
this.ReportPropertyChanged("SelectedPivotItemIndex");

(...)

ChangeSomeOtherViewModelProperty msg = new ChangeSomeOtherViewModelProperty { Property = newValueCalculatedBefore };
Messenger.Default.Send<ChangeSomeOtherViewModelProperty>(msg);
}
}
}

我现在能想到的唯一解决方案是在 ViewModel 中创建一个 LoadedEventHandler 并再次调用 SelectedPivotItemIndex setter 。不过,我真的不喜欢这样:
  • 这一次,setter 再次运行(这会创建一个相当大的集合并传递给消息)。不知道它是否真的会影响性能,但似乎仍然没有必要。
  • 其次,这对我来说似乎有点骇人听闻和容易出错,因为每个属性都必须在加载的事件中手动初始化。

  • 这个问题有没有比手动调用setter更好的解决方案?

    最佳答案

    我首先没有关于 viewmodel 的教程,但我敢肯定那里有很多例子。首先是 View 模型,然后您首先拥有 View 模型实例,然后让 wpf 创建 View (通过数据模板)。

    假设您的主视图应该显示带有 PivotBarEntries 的 View 。所以你现在要做的是在你的 mainviewmodel 中创建一个 pivotbarviewmodel(DI、MEF、new() 等等)。您的 mainviewmodel 将 pivotvw 公开为属性并将其绑定(bind)到您的 mainview 中的 ContentPresenter.Content 。至少您必须为您的 pivotvw DataType 创建一个 DataTemplate。

    <DataTemplate DataType="{x:Type local:PivotViewModel>
    <view:MyPivotView/>
    </DataTemplate>

    首先是关于 View 模型,您不再依赖 View 上的加载事件,因为您的虚拟机是首先创建的。

    当然,对于您的特定问题,您只需要确保应该创建所有听信使的组件(VM)

    你的xml
    <ContentPresenter Content="{Binding MyPivotDataVM}" />
    <ContentPresenter Content="{Binding MySomeOtherStuffVM}" />

    而不是先查看
    <pb:PivotBar ItemsSource="{Binding PivotBarEntries}" 
    SelectedItemIndex="{Binding SelectedPivotItemIndex, Mode=TwoWay}" />

    (...)

    <local:SomeOtherView />

    编辑:首先是 View 模型的非常简单的示例。 ps:我使用 DI 和 MEF 来创建我的对象路径。

    应用程序.xaml
    <Application x:Class="WpfViewModelFirst.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfViewModelFirst="clr-namespace:WpfViewModelFirst">
    <!--StartUp Uri is removed-->
    <Application.Resources>
    <!--comment these datatemplates and see what happens-->
    <DataTemplate DataType="{x:Type WpfViewModelFirst:PivotViewModel}">
    <WpfViewModelFirst:PivotView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type WpfViewModelFirst:OtherViewModel}">
    <WpfViewModelFirst:OtherView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type WpfViewModelFirst:OtherChildViewModel}">
    <WpfViewModelFirst:OtherChildView/>
    </DataTemplate>
    </Application.Resources>
    </Application>

    应用程序.xaml.cs
    public partial class App : Application
    {
    protected override void OnStartup(StartupEventArgs e)
    {
    //to be fair, sometimes i create the ApplicationRoot(JUST MainWindow with view first, and just the rest with viewmodel first.)
    var mainvm = new MainViewModel();
    var mainview = new MainWindow {DataContext = mainvm};
    this.MainWindow = mainview;
    this.MainWindow.Show();
    }
    }

    主视图.xaml
    <Window x:Class="WpfViewModelFirst.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
    <Grid.RowDefinitions>
    <RowDefinition Height="auto" />
    <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
    <ColumnDefinition />
    <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding MyProp}" Grid.ColumnSpan="2" Grid.Row="0"/>
    <ContentPresenter Content="{Binding MyPivot}" Grid.Row="1" Grid.Column="0" />
    <ContentPresenter Content="{Binding MyOther}" Grid.Row="1" Grid.Column="1" />
    </Grid>
    </Window>

    主视图模型.cs
    public class MainViewModel
    {
    public string MyProp { get; set; }
    public PivotViewModel MyPivot { get; set; }
    public OtherViewModel MyOther { get; set; }

    public MainViewModel()
    {
    this.MyProp = "Main VM";
    this.MyPivot = new PivotViewModel();
    this.MyOther = new OtherViewModel();
    }
    }

    透视 View 模型
    public class PivotViewModel
    {
    public string MyProp { get; set; }
    public ObservableCollection<string> MyList { get; set; }

    public PivotViewModel()//Dependency here with constructor injection
    {
    this.MyProp = "Test";
    this.MyList = new ObservableCollection<string>(){"Test1", "Test2"};
    }
    }

    其他 View 模型
    public class OtherViewModel
    {
    public string MyProp { get; set; }
    public OtherChildViewModel MyChild { get; set; }

    public OtherViewModel()
    {
    this.MyProp = "Other Viewmodel here";
    this.MyChild = new OtherChildViewModel();
    }
    }

    其他 child View 模型
    public class OtherChildViewModel
    {
    public string MyProp { get; set; }

    public OtherChildViewModel()//Dependency here with constructor injection
    {
    this.MyProp = "Other Child Viewmodel";
    }
    }

    透视图
    <UserControl x:Class="WpfViewModelFirst.PivotView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
    <Grid.RowDefinitions>
    <RowDefinition Height="auto" />
    <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <TextBlock Text="{Binding MyProp}" Grid.Row="0"/>
    <ListBox ItemsSource="{Binding MyList}" Grid.Row="1"/>
    </Grid>
    </UserControl>

    其他查看
    <UserControl x:Class="WpfViewModelFirst.OtherView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
    <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    </Grid.RowDefinitions>
    <TextBlock Text="{Binding MyProp}" Grid.Row="0" />
    <ContentPresenter Content="{Binding MyChild}" Grid.Row="1"/>
    </Grid>
    </UserControl>

    其他 subview
    <UserControl x:Class="WpfViewModelFirst.OtherChildView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
    <TextBlock Text="{Binding MyProp}" />
    </Grid>
    </UserControl>

    关于c# - 在 View 完成加载之前调用绑定(bind)属性的 setter ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10905918/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com