【问题标题】:Xamarin.Forms PCL MVVM Light > Custom Control > Best Practice?Xamarin.Forms PCL MVVM Light > 自定义控件 > 最佳实践?
【发布时间】:2015-09-01 00:11:09
【问题描述】:

Hy

我想分享我在使用 MVVM-Light 的 Xamarin PCL 项目中自定义 xamarin.forms 控件的方法。你怎么看?

自定义控件 -> PersonPanel.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="xxx.PersonPanel">
  <StackLayout Orientation="Vertical">
    <Label x:Name="titleLabel" Text="{Binding TitleLabel}"/>
    <Entry x:Name="filterText" Placeholder="{Binding FilterPlaceholderText}" Text="{Binding Filter.Lookup}" TextChanged="OnFilterTextChanged"/>
    <Label x:Name="resultText" Text="{Binding ResultText}" IsVisible="{Binding ResultTextVisible}"/>
  </StackLayout>
</ContentView>

代码隐藏 -> PersonPanel.xaml.cs:

public partial class PersonPanel : ContentView
{
    public PersonPanel()
    {
        InitializeComponent();

        //Init ViewModel
        BindingContext = ServiceLocator.Current.GetInstance<PersonPanelViewModel>();
    }

    private PersonPanelViewModel PersonPanelViewModel
    {
        get
        {
            return (PersonPanelViewModel)BindingContext;
        }
    }

    public string TitleLabel
    {
        get
        {
            return PersonPanelViewModel.TitleLabel;
        }
        set
        {
            PersonPanelViewModel.TitleLabel = value;
        }
    }

    public string FilterPlaceholderText
    {
        get
        {
            return PersonPanelViewModel.FilterPlaceholderText;
        }
        set
        {
            PersonPanelViewModel.FilterPlaceholderText = value;
        }
    }

    private void OnFilterTextChanged(object sender, EventArgs e)
    {
        PersonPanelViewModel.SearchCommand.Execute(null);
    }
}

ViewModel -> PersonPanelViewModel:

public class PersonPanelViewModel : ViewModelBase
{
    private IPersonService _personService;

    private decimal _personId = 0;
    private string _titleLabel = string.Empty;
    private string _filterPlaceholderText = string.Empty;
    private string _resultText = string.Empty;
    private bool _resultTextVisible = true;

    public PersonPanelViewModel(IPersonService personService)
    {
        _personService = personService;

        // Init Filter
        Filter = new PersonFilter();

        // Init Commands
        SearchCommand = new RelayCommand(Search);
    }

    public ICommand SearchCommand { get; set; }

    public PersonFilter Filter
    {
        get;
        private set;
    }

    public string ResultText
    {
        get
        {
            return _resultText;
        }
        set
        {
            Set(() => ResultText, ref _resultText, value);
        }
    }

    public bool ResultTextVisible
    {
        get
        {
            return _resultTextVisible;
        }
        set
        {
            Set(() => ResultTextVisible, ref _resultTextVisible, value);
        }
    }

    public string FilterPlaceholderText
    {
        get
        {
            return _filterPlaceholderText;
        }
        set
        {
            Set(() => FilterPlaceholderText, ref _filterPlaceholderText, value);
        }
    }

    public string TitleLabel
    {
        get
        {
            return _titleLabel;
        }
        set
        {
            Set(() => TitleLabel, ref _titleLabel, value);
        }
    }

    public decimal PersonId
    {
        get
        {
            return _PersonId;
        }
        set
        {
            Set(() => PersonId, ref _PersonId, value);
        }
    }

    private async void Search()
    {
        //Reset
        ResultText = string.Empty;
        ResultTextVisible = false;
        PersonId = 0;

        if (Filter.PersonLookup != null && Filter.PersonLookup.Length >= 3)
        {
            //Call to Person Service
            List<PersonResult> Person = await _personService.FindpersonByFilter(Filter);               

            if (Person.Count == 1)
            {
                PersonId = Person[0].PersonId;

                ResultText = Person[0].PersonName;
                ResultTextVisible = true;
            }
        }
    }
}

在另一个视图中使用控件:

<?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:components="clr-namespace:xxx.ViewElements.Components"
             x:Class="xxx.MainPage">
  <StackLayout Orientation="Vertical">
    <components:PersonPanel x:Name="personPanel" TitleLabel="Person" FilterPlaceholderText="Type your search criteria here..."/>
  </StackLayout>
</ContentPage>

我使用 Autofac 作为 IOC 容器。

你怎么看?我以正确的方式使用 MVVM(这对我来说很新)?

有没有更好的方法来处理从视图上的事件(TextChanged)调用命令?

Code-Behind(执行到 ViewModel 的路由)中的属性是什么?

编辑: 我将尝试描述我想要实现的目标:

  • 创建我们自己的控件(可在不同视图中重复使用,跨平台)-> PersonPanel.xaml
  • 在我们的 PCL 项目中用 XAML 编写,其中包含纯 Xamarin.Forms 控件
  • 控件应该有它自己的命令(搜索)和属性
  • 其中一个命令是使用服务
  • 控件应该通过 IOC 获得服务(作为接口)注入
  • 服务本身也在 PCL 项目中实现,并对 Web 服务进行 REST 调用
  • 调用结果设置为控件的属性 -> ResultText 属性
  • 结果对用户可见

-> 有了上面的实现,所有这些都可以,但我不确定这是否是正确的方法......

感谢您的帮助!

亲切的问候, 彼得

【问题讨论】:

    标签: xamarin mvvm-light xamarin.forms portable-class-library


    【解决方案1】:

    将事件映射到命令的方法与我将执行的完全一样。

    其余的有点令人困惑。一般模式是在您的控件中创建可绑定的属性,这些属性在宿主视图中实例化时会暴露给视图模型。一个非常基本的示例结构如下:

    public class TestLabelControl : Label
    {
        public static readonly BindableProperty TestTitleProperty = BindableProperty.Create< TestLabelControl, string> (p => p.TestTitle, null);
    
        public string TestTitle {
            get {
                return (object)GetValue (TestTitleProperty);
            }
            set {
                SetValue (TestTitleProperty, value);
            }
        }
    }
    
    public class TestContentPage : ContentPage
    {
        public TestContentPage()
        {
            var testLabel = new TestLabel();
            testLabel.SetBinding<TestContentPageViewModel>(TestLabel.TestTitleProperty, vm => vm.TestLabelTitle, BindingMode.Default);
            Content = testLabel;
        }
    }
    
    
    public class TestContentPageViewModel
    {
        public string TestLabelTitle{get;set;}
    
        public TestContentPageViewModel()
        {
            TestLabelTitle = "Something random";
        }
    }
    

    然后您将创建本地渲染器来处理每个平台上的绘图。

    通过遵循这种方法,您可以使代码保持分离和简洁。完成任务的方式似乎有点冗长,但它具有高度可扩展性和可配置性。

    【讨论】:

    • 为我的组件使用 ViewModel 是否正确,例如用于搜索的逻辑?此逻辑正在设置 Component-ViewModel (ResultText) 的另一个属性,该值如何暴露给视图?我的想法是拥有一个带有自己的 ViewModel 的组件(ContentView)以供重用。这个组件应该被其他控件使用。我想我了解代码隐藏文件中的可绑定属性,但我不确定它们是如何连接到 Component-ViewModel 的。我们不需要原生渲染器,我们只使用 xamarin.forms 组件。感谢您的帮助
    • 如果你想构建一个可重用的控件,它应该尽可能与相关视图的视图模型应用的逻辑无关。这是确保控件不会因条件代码而变得臃肿的唯一方法。也许,如果您详细说明您想要实现的目标,社区会提供更详细的示例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2010-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-26
    • 2012-08-19
    相关资源
    最近更新 更多