locked
Windows::UI::Xaml::Controls::ListView: When can I modify data in its Items property?

    Question

  • I have some code which changes the SubTitle of a element of a Windows::UI::Xaml::Controls::ListView called itemListView.

    It works when called from a OnNavigated( NavigationEvenArgs^ e ) handler, but fails when called from a handler of a button in the Appbar.  Presumably the difference is that itemListView was not displayed yet for OnNavigated, but has been for the Appbar button click handler.

    I expected the 2nd element to have a subtitle of "Changed in Edit Click:" but my actual result after clicking the Edit button in the app bar is:

    Is there some way to tell the itemListView that I have modified some part of the Items ?   Or perhaps am I completely confused?

    Here is the CPP code and XAML.   Thanks in advance for hints.

     
    MainPage::MainPage()
    {
    	InitializeComponent();
    
    	Item ^ item0 = ref new Item();	item0->Title= "Title for 0";	item0->Subtitle= "subtitle for 0";
    	itemListView->Items->Append( item0 );
    
    	Item ^ item1 = ref new Item();	item1->Title= "Title for 1";	item1->Subtitle= "subtitle for 1";
    	itemListView->Items->Append( item1 );
    
    }
    
    void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
    {
    	(void) e;	// Unused parameter
    
    	modifyListElement( "Changed in OnNavigatedTo" );	// DOES actually change the screen display.
    }
    
    void MainPage::Edit_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
    	modifyListElement( "Changed in Edit_Click" );		// FAILS to actually change the screen display.
    }
    
    void MainPage::modifyListElement( Platform::String ^ newSubTitle )
    {
    	Item ^ item= (Item ^) itemListView->Items->GetAt( 1 );
    
    	item->Subtitle= newSubTitle;
    }
    
    

    //
    // MainPage.xaml.h
    // Declaration of the MainPage class.
    //
    
    #pragma once
    
    #include "MainPage.g.h"
    
    using namespace Windows::UI::Xaml::Data;  // Makes event PropertyChangedEventHandler^ _PropertyChanged;  work, I don't know why.
    
    
    
    namespace ModifyListView
    {
    	/// <summary>
    	/// An empty page that can be used on its own or navigated to within a Frame.
    	/// </summary>
    	public ref class MainPage sealed
    	{
    	public:
    		MainPage();
    		void modifyListElement( Platform::String ^ newSubTitle );
    
    	protected:
    		virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
    	private:
    		void Edit_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
    	};
    
    
    
    	[Windows::UI::Xaml::Data::Bindable]
    	[Windows::Foundation::Metadata::WebHostHiddenAttribute]
    	public ref class Item sealed
    	{
    		Platform::String^  _Title;
    		Platform::String^  _Subtitle;
    		event PropertyChangedEventHandler^ _PropertyChanged; 
    
    	public:
    		Item() {};
    
    		void OnPropertyChanged(Platform::String^ propertyName)
    		{
    			PropertyChangedEventArgs^ pcea = ref new  PropertyChangedEventArgs(propertyName);
    			_PropertyChanged(this,pcea);
    		}
    
    		//Title
    		property Platform::String^ Title
    		{
    			Platform::String^ get()
    			{
    				return _Title;
    			}
    			void set(Platform::String^ value)
    			{
    				_Title = value;
    				OnPropertyChanged("Title");
    			}
    		}
    
    
    
    		//Subtitle
    		property Platform::String^ Subtitle
    		{
    			Platform::String^ get()
    			{
    				return _Subtitle;
    			}
    			void set(Platform::String^ value)
    			{
    				_Subtitle = value;
    				OnPropertyChanged("Subtitle");
    			}
    		}
    
    	};
    
    
    
    }
    
    <Page
        x:Class="ModifyListView.MainPage"
        IsTabStop="false"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:ModifyListView"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <ListView x:Name="itemListView"  Margin="120,0,0,80" >
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,10,10">
                            <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
                            <TextBlock Text="{Binding Subtitle}" Style="{StaticResource BodyTextStyle}" TextWrapping="NoWrap"/>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
    
        </Grid>
        
        <Page.BottomAppBar>
            <AppBar x:Name="bottomAppBar" Padding="10,0,10,0">
                <Grid>
                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                        <Button Style="{StaticResource EditAppBarButtonStyle}" Click="Edit_Click"/>
                    </StackPanel>
                </Grid>
            </AppBar>
        </Page.BottomAppBar>
    
    
    
        
    </Page>
    


    Wednesday, October 17, 2012 8:44 PM

Answers

  • Your ref class Item doesn't fire any PropertyChanged event because it doesn't fully implement INotifyPropertyChanged and ICustomPropertyProvider interfaces.

    Implementing these interfaces is a bit boring, so you can let Visual Studio do it for you. When you generate a new "Grid App Windows Store" project (File/New/Project/Windows Store/Grid App), Visual Studio generates a file which contains a class that implements these interfaces: BindableBase.cpp/.h. Then you should derive your Item class from BindableBase. That's all!

    Either copy/paste BindableBase.cpp .h into your project or copy/paste its code. Then derive your Item class from BindableBase. Remove _PropertyChanged and OnPropertyChanged members in your Item class since they are already implemented in BindableBase class.

    Here is how your Item class definition should look like:

    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
    {
    public:
    	virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
    	virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name){return nullptr;}
    	virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type){return nullptr;}
    	virtual Platform::String^ GetStringRepresentation(){return this->ToString();}
    	property Windows::UI::Xaml::Interop::TypeName Type {virtual Windows::UI::Xaml::Interop::TypeName get() { return this->GetType(); }}
    protected:
    	virtual void OnPropertyChanged(Platform::String^ propertyName)	{PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));}
    };
    
    [Windows::UI::Xaml::Data::Bindable]
    [Windows::Foundation::Metadata::WebHostHiddenAttribute]
    public ref class Item sealed : BindableBase
    {
    	Platform::String^  _Title;
    	Platform::String^  _Subtitle;
    public:
    	Item() {};
    	property Platform::String^ Title
    	{
    		Platform::String^ get()
    		{
    			return _Title;
    		}
    		void set(Platform::String^ value)
    		{
    			_Title = value;
    			OnPropertyChanged("Title");
    		}
    	}
    	property Platform::String^ Subtitle
    	{
    		Platform::String^ get()
    		{
    			return _Subtitle;
    		}
    		void set(Platform::String^ value)
    		{
    			_Subtitle = value;
    			OnPropertyChanged("Subtitle");
    		}
    	}
    };
    

    • Marked as answer by Jesse Jiang Tuesday, October 23, 2012 3:01 AM
    Tuesday, October 23, 2012 2:11 AM

All replies

  • I believe you will need to schedule the UI update to run on the UI thread.

    http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/e36f84be-db95-4764-951e-bfa662cdd11d


    Wednesday, October 17, 2012 8:55 PM
  • I also tried creating a member variable: 

    Platform::Collections::Vector<Object^> ^ collection;

    and initializing the ItemsSource as follows:

    collection= ref new Platform::Collections::Vector<Object^>();
    collection->Append( item0 );
    collection->Append( item1 );
    itemListView->ItemsSource= collection;
    
    Then I could update collection as follows:

    Item ^ collectionItem= (Item ^) collection->GetAt( 1 );
    collectionItem->Subtitle= newSubTitle;
    

    But the results are exactly the same.  The display never shows

    "Changed in Edit_Click"

    to i3v3I6:

    I'm already running on the UI thread. 

    I can get the dispatcher easily, but not sure what to do next.

    Windows::UI::Core::CoreDispatcher ^ dispatcher= itemListView->Dispatcher;
    dispatcher-> ????

     So I am still stuck.

    Thursday, October 18, 2012 2:53 PM
  • Your ref class Item doesn't fire any PropertyChanged event because it doesn't fully implement INotifyPropertyChanged and ICustomPropertyProvider interfaces.

    Implementing these interfaces is a bit boring, so you can let Visual Studio do it for you. When you generate a new "Grid App Windows Store" project (File/New/Project/Windows Store/Grid App), Visual Studio generates a file which contains a class that implements these interfaces: BindableBase.cpp/.h. Then you should derive your Item class from BindableBase. That's all!

    Either copy/paste BindableBase.cpp .h into your project or copy/paste its code. Then derive your Item class from BindableBase. Remove _PropertyChanged and OnPropertyChanged members in your Item class since they are already implemented in BindableBase class.

    Here is how your Item class definition should look like:

    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
    {
    public:
    	virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
    	virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name){return nullptr;}
    	virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type){return nullptr;}
    	virtual Platform::String^ GetStringRepresentation(){return this->ToString();}
    	property Windows::UI::Xaml::Interop::TypeName Type {virtual Windows::UI::Xaml::Interop::TypeName get() { return this->GetType(); }}
    protected:
    	virtual void OnPropertyChanged(Platform::String^ propertyName)	{PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));}
    };
    
    [Windows::UI::Xaml::Data::Bindable]
    [Windows::Foundation::Metadata::WebHostHiddenAttribute]
    public ref class Item sealed : BindableBase
    {
    	Platform::String^  _Title;
    	Platform::String^  _Subtitle;
    public:
    	Item() {};
    	property Platform::String^ Title
    	{
    		Platform::String^ get()
    		{
    			return _Title;
    		}
    		void set(Platform::String^ value)
    		{
    			_Title = value;
    			OnPropertyChanged("Title");
    		}
    	}
    	property Platform::String^ Subtitle
    	{
    		Platform::String^ get()
    		{
    			return _Subtitle;
    		}
    		void set(Platform::String^ value)
    		{
    			_Subtitle = value;
    			OnPropertyChanged("Subtitle");
    		}
    	}
    };
    

    • Marked as answer by Jesse Jiang Tuesday, October 23, 2012 3:01 AM
    Tuesday, October 23, 2012 2:11 AM
  • That works !  Thank you very much.   Considering "It is better to teach somehow how to fish, than to give them fish.", let me ask a few more questions.

    Now that you have "caught my fish", let me try to understand where I went wrong.

    I used the definition of Item from the ListViewSimple,

    http://code.msdn.microsoft.com/windowsapps/ListViewSimple-d5fc27dd/sourcecode?fileId=63946&pathId=892004812

    I did not understand this page, and it lacks C++ sample code too.

    http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh758320.aspx#Y2710

    Now how did you know where I went wrong?  Was there an sample I should have looked at?  Or some documentation that I either missed or failed to understand?

    Tuesday, October 23, 2012 3:22 AM
  • I'm not quite sure why PropertyChanged events work in the ListViewSimple code sample, and don't work in your code. Note that in the sample code, the Item elements are inserted in a IBindableObservableVector vector, which is databound to the control. But in your code, you add the Item elements directly to the control. In your MSDN link about databinding, the "binding to collection" chapter also says you should use a Vector and bind your list control to this vector. If you want to learn how to fish, you may want to find out if this difference matters, and understand how this really works (try/practice).

    I think the documentation about C++/XAML programming is very poor. In fact the only documentation are the code samples.

    I proposed you a way to make your code work because I have seen similar code in the default Visual Studio project templates. I guess there is another way to make your code work, since the ListViewSimple doesn't use the BindableBase class. Let us know if you find it.

    Tuesday, October 23, 2012 9:39 PM