MVVM add elements dynamically to scrollview

梦想的初衷 提交于 2021-01-27 19:55:20

问题


Apologies ahead of time if this was asked before. The closest thing I found was (How to use vertical Scroll view to show binding data), which looks promising, though I am not sure if it will work because my understanding is that a template, as the name and definition would imply, has to follow a specific pattern and thus can't be dynamic.

My issue: I am trying to rewrite some of our code to more closely follow MVVM patterns, this of course means making the view as dumb as possible. In my view (xaml.cs) I have code that does the following:

 foreach(var tuple in itemsToAdd)
            {
                var formEntry = formEntries.First(x => x.FormEntryId == tuple.Item1);
                var controlType = formatting.GetControlType(formEntry.FormAnswerName,
                                                         formEntry.CustomEntryIdentifier);

                if (formEntry.Type == 0) {
                    switch (controlType) {
                        case CustomControl.Duration:
                            CustomFields.Children.Add(new DurationView(formEntry.FormEntryId, viewModel));
                            break;
                        case CustomControl.TextField:
                        case CustomControl.InputField:

                            CustomFields.Children.Add(new InputFieldView(formEntry.FormEntryId, viewModel));
 .....//continues like this for different controls.

The list itemsToAdd is defined in my view model, its job is to figure out which elements from a user generated template need to be displayed. This is of course fine, since per my understanding of MVVM, the view model is what needs to control the behaviors (in this case, what to display) and the UI simply handles displaying the fields.

I was wondering though if it could be possible to also let the view model handle, in essence, setting the item source of the scroll view as we do when we create an item source for a list view (say like a list of available templates), as since each of the controls is a custom view that we made, I don't see a doable way of achieving this as since it can't (from my understanding) follow a template since each control is different, and I don't want to have my view model returning an actual view object as that would violate MVVM principles.

Really in essence is: Am I misunderstanding MVVM in Xamarin, and is it alright for the xaml code behind to add elements like what I show in the code snippet.

Apologies if it is unclear what I am asking for!


回答1:


I am not sure I understand the real problem, are you trying to show a different UI for each itemsToAdd element according to the Type property?

If it is the problem, you can bind the entire collection to the ListView control (or whatever you are using) and use a DataTemplateSelector to choose the UI you want. Reference

View You can refer to the previous link. It would be like:

<ListView ItemsSource="{Binding FormEntries}" DataTemplateSelector="{StaticResource dataTemplateSelectorName}"/>

Ensure your page data context is set to the view model

ViewModel Assuming your object type is named FormEntry

private ObservableCollection<FormEntry> _formEntries;
public ObservableCollection<FormEntry> FormEntries
{
    get => _formEntries;
    set
    {
        _formEntries = value;
        // Call the 'RaisePropertyChanged' of the framework you are using
    }
}

...
FormEntries = new ObservableCollection<FormEntry>(itemsToAdd);
...

DataTemplateSelector Refer to the link to build a class that extends DataTemplateSelector and override the OnSelectTemplate method to determine the business logic to choose the UI template you want




回答2:


So I was able to figure out how to do it, many thanks to Krusty for pointing me towards the right sources.

I had to create my DataTemplateSelector,

public class MyTemplate: DataTemplateSelector
    {
        public DataTemplate InputFieldTemplate { get; set; }
        public DataTemplate EmptyTemplate { get; set; }
        public DataTemplate DurationTemplate { get; set; }
        public DataTemplate SignatureDetailTemplate {get; set;}
        public DataTemplate SignaturePhoneTemplate { get; set; }
        public DataTemplate DateTemplate { get; set; }


        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
            try
            {
                ControlsViewModel viewModel = item as ControlsViewModel;

                if (viewModel == null)
                {
                    return EmptyTemplate;
                }


                if (viewModel.ControlType == Enums.CustomControl.InputField || viewModel.ControlType == Enums.CustomControl.TextField)
                    return InputFieldTemplate;
                if (viewModel.ControlType == Enums.CustomControl.Duration)
                {
                    return DurationTemplate;
                }
                if (viewModel.ControlType == Enums.CustomControl.Signature)
                {
                    if(string.IsNullOrEmpty(viewModel.Disclaimer))
                    {
                        return SignatureDetailTemplate;
                    }

                    return SignaturePhoneTemplate;
                }



                return EmptyTemplate;
            }
            catch
            {
                throw;
            }
        }
    }

Then I create the corresponding Dictionary for it (In this case, ResourceDictionary.xaml)

<DataTemplate x:Key="EmptyTemplate">
    <ViewCell>
        <BoxView></BoxView>
    </ViewCell>
</DataTemplate>


<DataTemplate x:Key="InputFieldTemplate">
    <ViewCell>


        <StackLayout Orientation="Vertical" 
                         Padding="0, 20, 0, 0"
                         HorizontalOptions="FillAndExpand"
                         VerticalOptions="StartAndExpand">

        <Label Text="{Binding Label}"
                      FontAttributes="Bold"
                      TextColor="Black"
                      FontSize="14"
                      VerticalOptions="Start"
                      HorizontalOptions="FillAndExpand"/>


        <StackLayout
            HorizontalOptions="FillAndExpand"
            VerticalOptions="Start"
            Spacing="0"
            Orientation="Vertical">

            <Editor

                Text="{Binding Answer}"
                TextColor="Black"
                AutoSize="TextChanges" 
                IsTextPredictionEnabled="False"
                IsSpellCheckEnabled="False"
                BackgroundColor="White"
                FontSize="14"
                VerticalOptions="Start"
                HorizontalOptions="FillAndExpand"/>

            <BoxView HeightRequest="1"
                 BackgroundColor="{Binding FieldColor}" 
                 Margin="0"
                 HorizontalOptions="FillAndExpand"
                 VerticalOptions="Start"/>

        </StackLayout>
    </StackLayout>
    </ViewCell>
</DataTemplate>    

Finally, in the view that I want to use it, I add it as follows:

<ListView ItemsSource="{Binding NoteObjects}" ItemTemplate="{StaticResource DataTemplateSelector}"/>


来源:https://stackoverflow.com/questions/60209412/mvvm-add-elements-dynamically-to-scrollview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!