将 MAUI ControlTemplate 绑定到 ContentPage 视图模型

问题描述 投票:0回答:2

我正在尝试添加一个通用控件,我希望出现在应用程序中每个内容页面的底部(这些都将位于 TabBar 内)。我在 App.xaml 中创建了一个 ControlTemplate,并且出现了放置在底部的选取器,但 ItemsSource 属性绑定不起作用(没有可见的项目)。

我不知道如何让它发挥作用。我是 Xamarin/MAUI 的新手,如果我要实现这一目标的方向错误,我愿意接受不同方法的建议。

我尝试在 XAML 中使用

TemplateBinding
而不是
Binding
,并且我还在 App.xaml.cs 和 AppShell.xaml.cs 代码隐藏文件中放置了相同的属性,以防绑定被重定向到那里,这没有什么区别。我还从
Environments
属性开始,只是类型为
Env[]
,然后切换到
ObservableCollection
作为故障排除措施(即使集合显然是静态的)。

应用程序.xaml

<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:My.App"
             x:Class="My.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                <ResourceDictionary Source="Resources/Styles/Styles.xaml" />
                <ResourceDictionary>
                    <ControlTemplate x:Key="InputPageTemplate">
                        <VerticalStackLayout BindingContext="{Binding Source={RelativeSource TemplatedParent}}">
                            <ContentPresenter />
          <!-- ********************  BINDINGS NOT WORKING ******************** -->
                            <Picker ItemsSource="{Binding Environments}"
                                    SelectedItem="{Binding AppConfig.Environment}" />
                        </VerticalStackLayout>
                    </ControlTemplate>
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

我的页面.cs

public class MyPage : ContentPage
{
    public MyPage()
    {
        if (!Application.Current!.Resources.TryGetValue("InputPageTemplate", out var resource) ||
            resource is not ControlTemplate template)
        {
            throw new Exception("Missing InputPageTemplate control template");
        }

        var appConfig = new AppConfig { Environment = Env.B };
        ViewModel = new MyViewModel(appConfig);
        BindingContext = ViewModel;
        ControlTemplate = template;
    }
}

MyViewModel.cs

public class MyViewModel
{
    public MyViewModel(AppConfig appConfig)
    {
        AppConfig = appConfig;
    }

    public AppConfig AppConfig { get; }
    public ObservableCollection<Env> Environments => new(Enum.GetValues<Env>());
}

AppConfig.cs

public class AppConfig : INotifyPropertyChanged
{
    private Env _environment;
    public Env Environment
    {
        get => _environment;
        set
        {
            _environment = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string name = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

环境.cs

public enum Env
{
    A,
    B
}
c# .net xaml data-binding maui
2个回答
5
投票

让模板与内容页面共享绑定上下文的方法是在绑定中包含路径,如下所示:

<VerticalStackLayout
   BindingContext="{Binding Source={RelativeSource TemplatedParent}, Path=BindingContext}">
<!--  Add this:                                                      ^^^^^^^^^^^^^^^^^^^     -->

0
投票

好吧,这会很长。

当您想在 ControlTemplate 中绑定某些内容时,不要直接绑定它。 您必须使用不同的来源。

有几种方法可以做到这一点。一种是使用TemplateBinding 这个词。 另一种是将 BindingContext 设置为模板化父级。

例如:

<Control Template...
   <Grid BindingContext="{Binding Source={RelativeSource TemplatedParent}"...
       <Button Command="{Binding MyCommand}"...

当您按下该按钮时,绑定将执行 MyCommand,无论该控件在运行时是什么。

因此,如果在您的 ViewModel 中您有 GetDataCommand,您将在您的 View 中执行此操作:

<mycontrols:MyView MyCommand="{Binding GetDataCommand}...

这里的处理是让这个自定义控件类 MyView 具有 BindableProperty。这将允许您在 ViewModel 和 ControlTemplate 之间进行绑定。

public class MyView : ContentView{
     public static readonly MyCommandProperty = ...
     public ICommand MyCommand...
}

当我不确定某件事是如何完成时,我通常会点击F12并浏览平台代码。首先,您了解一切是如何运作的,其次您学习如何自己做。

另外,我建议您使用依赖注入,并限制构造函数的使用。我还推荐 CommunityToolkit.MVVM。它将节省您实施 INotifyPropertyChanged 的时间。 (以及错误操作可能导致的错误)。

© www.soinside.com 2019 - 2024. All rights reserved.