【问题标题】:Understanding MVVM and ObjectDetailViewModel in Udemy Course在 Udemy 课程中了解 MVVM 和 ObjectDetailViewModel
【发布时间】:2020-06-11 01:07:38
【问题描述】:

我一直在为 Xamarin 做一门课程,我在 MVVM 部分,所有 xaml.cs 代码都移到 ViewModels 中。

这是课程(第 104、105、106 课):[url]https://www.udemy.com/course/complete-xamarin-developer-course-ios-and-android[/url]

到目前为止,我已经能够理解 MVVM 的概念,但是,这门课程似乎做错了一些事情(我相信),并且以前讲座中的一些代码不在新讲座中(我们' 将暂时不理会这种烦恼),继续前进,我不确定如何正确编码。我也明白我目前正在展示一个简单的用例,所以让我们认为 Post 是一个不应该一遍又一遍地重新创建的巨大对象。

例如我有一个PostDetailPage.xaml(.cs),在xaml中,我目前有以下(注意大部分Mode=TwoWay可能使用不正确):

<ContentPage.Content>
    <StackLayout Margin="10">
        <Entry x:Name="experienceEntry" 
               Text="{Binding Experience, Mode=TwoWay}"
               Margin="-5"></Entry>
        <label x:name="venuename"
               text="{binding post.venuename, mode=onetime}"
               fontattributes="bold"/>
        <label x:name="categoryname"
               text="{binding post.categoryname, mode=onetime}"/>
        <label x:name="address"
               text="{binding post.address, mode=onetime}"/>
        <label x:name="coordinatelabel"
               text="{binding coordinates, mode=onetime}"/>
        <label x:name="distance"
               text="{binding post.distance, mode=onetime, stringformat='{0:0}'}"/>

        <Button Text="Update"
                x:Name="updateButton"
                Command="{Binding UpdatePostCommand}"
                CommandParameter="{Binding Post}" />

    </StackLayout>
</ContentPage.Content>

代码如下:

public partial class PostDetailPage : ContentPage
{
    PostDetailViewModel viewModel;

    public PostDetailPage(Post selectedPost)
    {
        InitializeComponent();

        viewModel = new PostDetailViewModel(selectedPost);
        BindingContext = viewModel;
    }
}

然后我有一个不完整的 PostDetailViewModel:

public class PostDetailViewModel : INotifyPropertyChanged
{
    public UpdatePostCommand UpdatePostCommand { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    public PostDetailViewModel(Post post)
    {
        UpdatePostCommand = new UpdatePostCommand(this);
        SelectedPost = post;
    }

    //Can this become another ViewModel or ?
    private Post selectedPost;
    public Post SelectedPost
    {
        get { return selectedPost; }
        set 
        {
            selectedPost = value;
            OnPropertyChanged("Post");
        }
    }

    private string experience;
    public string Experience
    {
        get { return experience; }
        set
        {
            experience = value;
            UpdatePostObject();
            OnPropertyChanged("Experience");
        }
    }

    private void UpdatePostObject() {
        Post = new Post() {
            Experience = experience,

            //many more properties are needed here
            //seems like if the solution was to grow,
            //this would become unmanagable or something

        }
    }

    private void UpdatePostObject()
    {
        //This is used to Trigger the change on Post
        //I don't think this is the best way (hence my question(s))
        SelectedPost = new Post()
        {
            Experience = experience
        };
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public async void Update()
    {
        await App.Current.MainPage.Navigation.PushAsync(new HistoryPage());
    }
}

在课程中,讲师建议当Experience 发生更改(或与Post 相关的任何其他属性)时,我们一次又一次地重新创建Post 对象以触发CanExecute 和关联的@ 987654330@。每次更改后必须一次又一次地重新创建 Post 对象似乎是不正确的,所以最好只更新需要的对象吗?

所以,我要问(或了解)的是......

  1. 我是否应该实现一个通用的PostViewModel,当Experience 发生变化时更新它,并以某种方式将ICommand 附加到它,如果是这样,这样的东西会是什么样子?当然,如果这不正确,请尽可能指出正确的方向。

  2. 有没有类似OnObjectChanged 的方法可以使用?我在处理收藏时知道ObservableCollection

如果还有什么我可以提供的,请告诉我。

谢谢 德里克

【问题讨论】:

    标签: c# mvvm xamarin.forms inotifypropertychanged


    【解决方案1】:

    首先我打不开你的网址,这门课不见了。

    我是否应该实现一个通用的 PostViewModel,它会在体验发生变化时更新,并以某种方式将 ICommand 附加到它,如果是这样,这样的东西会是什么样子?当然,如果这不正确,请尽可能指出正确的方向。

    对于UpdatePostObject方法,你不需要new一个Post对象,如果你想为selectedPost对象设置新值,直接如下代码设置即可,

       private void UpdatePostObject()
            {
                selectedPost.Experience = experience;
                selectedPost.address= "new Adree";
    
    
    
                selectedPost.categoryname = "new CateGoryName";
                selectedPost.distance = "41";
                selectedPost.venuename = "new venu";
    
                    //many more properties are needed here
                    //seems like if the solution was to grow,
                    //this would become unmanagable or something
    
               // };
            }
    

    如果你想实现点击命令,你只需将PostDetailViewModel中的public ICommand UpdatePostCommand { get; set; }清除即可。然后在您的PostDetailViewModel 的构造函数中实现它。这是我关于PostDetailViewModel的所有代码

       public class PostDetailViewModel 
        {
            //: INotifyPropertyChanged
            public ICommand UpdatePostCommand { get; set; }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public PostDetailViewModel(Post post)
            {
                UpdatePostCommand = new Command<Post>((key) => {
    
    
                    key.categoryname = "Command categoryname";
                    key.distance = "31";
                    key.address = "Command address";
                    key.venuename = "Command  venuename";
                    key.Experience = "Command Experience";
    
                });
    
    
    
    
    
    
                SelectedPost = post;
            }
    
            //Can this become another ViewModel or ?
            private Post selectedPost;
            public Post SelectedPost
            {
                get { return selectedPost; }
                set
                {
                    selectedPost = value;
                 //   OnPropertyChanged("SelectedPost");
                }
            }
    
            private string experience;
            public string Experience
            {
                get { return experience; }
                set
                {
                    experience = value;
                    UpdatePostObject();
                  //  OnPropertyChanged("Experience");
                }
            }
    
            private void UpdatePostObject()
            {
                selectedPost.Experience = experience;
                selectedPost.address= "new Adree";
    
    
    
                selectedPost.categoryname = "new CateGoryName";
                selectedPost.distance = "41";
                selectedPost.venuename = "new venu";
    
                    //many more properties are needed here
                    //seems like if the solution was to grow,
                    //this would become unmanagable or something
    
               // };
            }
    
            //private void UpdatePostObject()
            //{
            //    //This is used to Trigger the change on Post
            //    //I don't think this is the best way (hence my question(s))
            //    SelectedPost = new Post()
            //    {
            //        Experience = experience
            //    };
            //}
    
            //public event PropertyChangedEventHandler PropertyChanged;
    
            //protected virtual void OnPropertyChanged(string propertyName)
            //{
            //    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            //}
            public async void Update()
            {
                await App.Current.MainPage.Navigation.PushAsync(new HistoryPage());
            }
        }
    }
    
    
    

    如果你想在运行时实现属性变化,你应该为你的POST模型中的所有属性实现INotifyPropertyChanged接口。

       public class Post:INotifyPropertyChanged
        {
         //   public string _venuename { get; set; }
          //  public string categoryname { get; set; }
            string _categoryname;
            public string categoryname
            {
                set
                {
                    if (_categoryname != value)
                    {
                        _categoryname = value;
                        OnPropertyChanged("categoryname");
    
                    }
                }
                get
                {
                    return _categoryname;
                }
            }
    
            //public string address { get; set; }
    
    
            string _address;
            public string address
            {
                set
                {
                    if (_address != value)
                    {
                        _address = value;
                        OnPropertyChanged("address");
    
                    }
                }
                get
                {
                    return _address;
                }
            }
    
    
           // public string Experience { get; set; }
    
            string _experience;
            public string Experience
            {
                set
                {
                    if (_experience != value)
                    {
                        _experience = value;
                        OnPropertyChanged("Experience");
    
                    }
                }
                get
                {
                    return _experience;
                }
            }
    
          //  public string distance { get; set; }
    
            string _distance;
            public string distance
            {
                set
                {
                    if (_distance != value)
                    {
                        _distance = value;
                        OnPropertyChanged("distance");
    
                    }
                }
                get
                {
                    return _distance;
                }
            }
    
            string _venuename;
            public string venuename
            {
                set
                {
                    if (_venuename != value)
                    {
                        _venuename = value;
                        OnPropertyChanged("venuename");
    
                    }
                }
                get
                {
                    return _venuename;
                }
            }
    
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    你的布局有一些错误,我改变了。

       <ContentPage.Content>
            <StackLayout Margin="10">
                <Entry x:Name="experienceEntry" 
                   Text="{Binding Experience, Mode=TwoWay}"
                   Margin="-5">
    
                </Entry>
    
                <Label x:Name="venuename"
                   Text="{Binding SelectedPost.venuename, Mode=TwoWay}"
                       FontAttributes="Bold"
                  />
                <Label x:Name="categoryname"
                   Text="{Binding SelectedPost.categoryname, Mode=TwoWay}"/>
                <Label x:Name="address"
                   Text="{Binding SelectedPost.address, Mode=TwoWay}"/>
                <Label x:Name="coordinatelabel"
                   Text="{Binding SelectedPost.coordinates, Mode=TwoWay}"/>
                <Label x:Name="distance"
                   Text="{Binding SelectedPost.distance, Mode=TwoWay, StringFormat='{0:0}'}"/>
    
                <Button Text="Update"
                    x:Name="updateButton"
                    Command="{Binding UpdatePostCommand}"
                    CommandParameter="{Binding SelectedPost}" />
    
            </StackLayout>
        </ContentPage.Content>
    
    

    这是我的布局背景代码。

       public MainPage()
            {
                InitializeComponent();
                Post selectedPost=new Post() { 
                    address= "address", 
                    categoryname= "categoryname", 
                    distance= "21", 
    
                    venuename= "venuename" };
                PostDetailViewModel viewModel = new PostDetailViewModel(selectedPost);
                BindingContext = viewModel;
            }
        }
    

    是否可以使用某种类型的类似 OnObjectChanged 的​​方法?我在处理集合时知道 ObservableCollection。

    在您的PostDetailViewModel 中,没有必要实现这一点。如果你想用ObservableCollection添加Post,就达到INotifyPropertyChanged,如果你Post项目会增加或减少,它会自动改变,但如果Post的值会改变,它会不变,你必须在你的Post模型中实现INotifyPropertyChanged

    这是我运行的GIF。一般情况下,它有一些默认值,如果我在Entry中输入这个值,post的这个值会被改变,如果我点击Button,post的值会被改变为另一个值

    这里有一篇关于它的有用文章,你可以参考一下。 https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm

    【讨论】:

    • 这个问题有更新吗?如果这个答案有帮助,请采纳(点击这个答案左上角的☑️)会对有类似问题的其他人有所帮助
    猜你喜欢
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 1970-01-01
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多