How to dynamic add FlipView item (text content) when swipe?

467 Views Asked by At

I want to dynamically add new flip view item in UWP. So that I can add infinite items to my flip view. For example, to fetch hot news and show them one by one in flip view.

I find some similar code from Internet and modify it a little. Below is the xaml code and cs code behind. As you can see, I want to use FlipView_SelectionChanged() to dynamic add new flip view item but failed. I expect to add new flip view item with text content like Name new 3, Name new 4...

XAML:

<Grid Name="grid">
    <FlipView Name="flipView" ItemsSource="{Binding ModelItems}">
        <FlipView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding Name}" FontSize="60" />
                </StackPanel>
            </DataTemplate>
        </FlipView.ItemTemplate>
    </FlipView>
</Grid>

C#:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        flipView.SelectionChanged += FlipView_SelectionChanged;
    }

    private void FlipView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        Debug.WriteLine("flipview selection changed...index = " + flipView.SelectedIndex);

        BaseViewModel bv = new BaseViewModel();
        bv.ModelItems.Add(new BaseViewModelItem() { Name = "Name new " + flipView.SelectedIndex });

        //grid.DataContext = bv;

        Debug.WriteLine("flipview selection changed...count = " + bv.ModelItems.Count);
    }

    protected async override void OnNavigatedTo(NavigationEventArgs e)
    {
        grid.DataContext = new BaseViewModel();
    }

    public class BaseViewModelItem
    {
        public string Name { get; set; }
    }

    public class BaseViewModel
    {
        public ObservableCollection<BaseViewModelItem> ModelItems { get; set; }
        public BaseViewModel()
        {
            ModelItems = new ObservableCollection<BaseViewModelItem>();
            ModelItems.Add(new BaseViewModelItem() { Name = "Name 1" });
            ModelItems.Add(new BaseViewModelItem() { Name = "Name 2" });
            ModelItems.Add(new BaseViewModelItem() { Name = "Name 3" });
        }
    }
}
2

There are 2 best solutions below

0
Shashank Prabhu On BEST ANSWER

I have modified the code that you have shared. I suggest you to use data binding instead of FlipView_SelectionChanged(), whenever item added to the ModelItems it update the element which it is bounded. I hope this will be helpful.

<Grid Name="grid">
    <Grid.RowDefinitions>
        <RowDefinition Height="20"/>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <Grid Grid.Row="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" Content="Add New FlipView Item" Click="Button_Click"/>
        <DockPanel Grid.Column="1">
            <TextBlock>Flipview Item Count : </TextBlock>
            <TextBlock Text="{Binding ModelItems.Count}"/>
        </DockPanel>
    </Grid>

    <FlipView Grid.Row="1" Name="flipView" ItemsSource="{Binding ModelItems,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
        <FlipView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Vertical">
                    <DockPanel>
                        <TextBlock Text="Index:" FontSize="20"/>
                        <TextBlock Text="{Binding Index}" FontSize="60" />
                    </DockPanel>
                    <DockPanel>
                        <TextBlock Text="Name:" FontSize="20"/>
                        <TextBlock Text="{Binding Name}" FontSize="60" />
                    </DockPanel>
                </StackPanel>
            </DataTemplate>
        </FlipView.ItemTemplate>
    </FlipView>
</Grid>

Logical Part code

//Interaction logic for MainWindow.xaml
public partial class MainWindow : Window
{
    public BaseViewModel ViewModel { get; set; } = new BaseViewModel();

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = ViewModel;
    }

    static int k = 3;
    private void Button_Click(object sender, RoutedEventArgs e) //can also implement using ICommand instead of event
    {
        this.ViewModel.ModelItems.Add(new BaseModelItem { Index = k, Name = "Name" + ++k });
    }
}

//--------Model------------
public class NotifyPropertyChanged : System.ComponentModel.INotifyPropertyChanged
{
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyRaised(string propertyname)
    {
        PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyname));
    }
}

public class BaseModelItem : NotifyPropertyChanged
{
    string _name = string.Empty;
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyRaised("Name"); }
    }

    int _index = 0;
    public int Index
    {
        get { return _index; }
        set { _index = value; OnPropertyRaised("Index"); }
    }
}

//--------ViewModel------------
public class BaseViewModel:NotifyPropertyChanged
{
    System.Collections.ObjectModel.ObservableCollection<BaseModelItem> _modelItems = new System.Collections.ObjectModel.ObservableCollection<BaseModelItem>();
    public System.Collections.ObjectModel.ObservableCollection<BaseModelItem> ModelItems
    {
        get { return _modelItems; }
        set { _modelItems = value; OnPropertyRaised("ModelItems"); }
    }

    public BaseViewModel()
    {
        ModelItems = new System.Collections.ObjectModel.ObservableCollection<BaseModelItem>();
        ModelItems.Add(new BaseModelItem() { Name = "Name 1", Index = 0 });
        ModelItems.Add(new BaseModelItem() { Name = "Name 2", Index = 1 });
        ModelItems.Add(new BaseModelItem() { Name = "Name 3", Index = 2 });
    }
}
0
Martin Zikmund On

There are two things in play here. First, you don't need to assign a new instance of the whole view model to make a change - instead, you can just retrieve the existing view model and add the new item:

var vm = (BaseViewModel)grid.DataContext;
bv.ModelItems.Add(
    new BaseViewModelItem() { 
      Name = "Name new " + flipView.SelectedIndex 
    });

Then there is an issue with when the event is attached. If you attach it right in the constructor, it will be fired immediately when the DataContext is set the first time and the items are being added to the ObservableCollection - and an attempt to add an item in this moment will make you end up with a catastrophic failure. Instead, you should attach the event only after the DataContext is set - so remove the flipView.SelectionChanged += FlipView_SelectionChanged; from the constructor to OnNavigatedTo after setting the data context:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    grid.DataContext = new BaseViewModel();
    flipView.SelectionChanged += FlipView_SelectionChanged;
}

This way the items will be properly added when selection is changed.