【问题标题】:How to bind to reusable Content Views in Xamarin Forms?如何绑定到 Xamarin 表单中的可重用内容视图?
【发布时间】:2019-12-07 16:48:23
【问题描述】:

在接近 Xamarin 形式的跨平台开发时,我在为可重用控件的定义而苦苦挣扎。 作为第一个非常基本的示例,我开发了一个虚拟组件,如下所示:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vmBase="clr-namespace:TestApp.ViewModels.Base"
             mc:Ignorable="d"
             vmBase:ViewModelLocator.AutoWireViewModel="True"
             x:Class="TestApp.Views.Templates.HashtagContainerTemplateView"
             x:Name="this">
  <ContentView.Content>
        <StackLayout BindingContext="{Reference this}">
            <Label Text="{Binding Test}"/>
        </StackLayout>
    </ContentView.Content>
</ContentView>

后面的代码在哪里:

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestApp.Views.Templates
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class HashtagContainerTemplateView : ContentView
    {

        public static readonly BindableProperty TestProperty = BindableProperty.Create(
            propertyName: nameof(Test),
            returnType: typeof(string),
            declaringType: typeof(HashtagContainerTemplateView),
            defaultBindingMode: BindingMode.TwoWay,
            defaultValue: "I am the default value",
            propertyChanged: TestPropertyChanged);

        private static void TestPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            System.Diagnostics.Debugger.Break();    // This is called only when binding with constant values
        }

        public string Test
        {
            get => (string)GetValue(TestProperty);
            set
            {
                SetValue(TestProperty, value);
                System.Diagnostics.Debugger.Break();    // This is never called
            }
        }

        public HashtagContainerTemplateView()
        {
            InitializeComponent();
        }
    }
}

我正在尝试通过将 Test 属性与父 ViewModel 设置的值绑定来将此视图加载到页面中,如下所示:

namespace TestApp.ViewModels
{
    public class MainViewModel : BaseViewModel
    {

        private string _testString;

        public string TestString 
        { 
            get => _testString; 
            set
            {
                _testString = value;
                RaisePropertyChanged();
            }
        }

        public MainViewModel()
        {
            
        }


        // This is called by the View Model Locator
        public override async Task InitializeAsync(object navigationData)
        {
            TestString = "I am the binded string";

            await base.InitializeAsync(navigationData);
        }
    }
}

最后,视图被加载到父页面中:

<templates:HashtagContainerTemplateView Test="I am a costant string"/><!--This Works-->  
<templates:HashtagContainerTemplateView Test="{Binding TestString}"/> <!--Not Working-->

运行应用程序时,显示的标签为:
I am a costant string 符合预期
I am the default value 是属性的默认值,而不是我通过绑定传递的值

经过一些调试后,我意识到只有在与常量值绑定时才会调用 TestPropertyChanged,并且永远不会调用 Test setter - 请参阅上面代码中的断点 - 所以我认为这就是重点......

我知道有很多这样的话题,即使在 SO 上也是如此,但我真的无法让它发挥作用......我相信我错过了一些非常简单的东西......

最后说明:我使用 Microsoft eShopOnContainers 项目作为参考,因此我使用 View Model Locator 方法。这就是为什么初始化不在 ctor 中而是在 InitializeAsync 函数中的原因。

Microsoft 本身在文档中有关于 Content Views 的部分,但没有使用任何绑定...

【问题讨论】:

  • 与内容视图绑定相关的一切看起来都是正确的。您是否尝试将TestString 属性绑定到常规标签,只是为了检查视图模型是否正确连接?也不清楚为什么您的内容视图中需要vmBase:ViewModelLocator.AutoWireViewModel="True"
  • 您的问题解决了吗?
  • @ValeriyKovalenko View Model Locator 实际上是问题所在!删除那个顺便说一句没用的,解决了问题。

标签: xaml xamarin mvvm xamarin.forms


【解决方案1】:

您需要更改以下内容:-

Text="{Binding Test,  Source={x:Reference this}}"

并将 PropertyChanged 移除为 null。

【讨论】:

    【解决方案2】:

    我使用您的代码,它在我这边运行良好。我猜你忘记在父页面中设置BindingContext

    这是我的使用方法:

    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
    
            BindingContext = new MainViewModel();
        }
    }
    
    public class MainViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private string _testString;
    
        public string TestString
        {
            get => _testString;
            set
            {
                _testString = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("TestString"));
                }
            }
        }
    
        public MainViewModel()
        {
            TestString = "I am the binded string";
        }
    
    }
    

    在 Xaml 中:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:d="http://xamarin.com/schemas/2014/forms/design"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:templates="clr-namespace:App593"
                 mc:Ignorable="d"
                 x:Class="App593.MainPage">
    
        <StackLayout>
            <!-- Place new controls here -->
            <templates:View1 Test="I am a costant string"/>
            <templates:View1 Test="{Binding TestString}"/>
        </StackLayout>
    
    </ContentPage>
    

    记得在父页面中添加BindingContext = new MainViewModel();。我项目中的其他代码和你的完全一样。

    【讨论】:

      【解决方案3】:

      问题在于 View Model Locator,它弄乱了绑定上下文,只是复制/粘贴的剩余部分。

      删除 vmBase:ViewModelLocator.AutoWireViewModel="True" 从 Xaml 解决了这个问题!

      感谢@ValeriyKovalenko 让我发现了它!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-17
        • 1970-01-01
        • 1970-01-01
        • 2021-10-02
        • 1970-01-01
        • 2015-04-22
        相关资源
        最近更新 更多