locked
Save Textbox Value in ListView ItemTemplate RRS feed

  • Question

  • Hello.

    I Have this ListView and using a ItemTemplate that has a textbox. Is there possible to save the value I give it?

    Am I correct if I think its a bit tricky when its in a itemtemplate.

    Saturday, July 19, 2014 9:12 AM

Answers

  • Hi Alexander,

    I believe I misunderstood what you were trying to say before. When you said "this does not change the value," I assumed you were referring to the fact that the new Subtitle value did not get saved in the JSON file. This is correct. However, the Subtitle value does get changed in memory. I thought that the value was not getting changed even in memory before.

    Doing what I said about copying all the data into a custom class would probably be your best bet. If you're looking for a more crude solution, you can get the TextBox Text values, store them in a custom class, and load them separately from the rest of your data. I figure this is at least another option if you don't want to go the first route. Here's a quick sample of how you can get a TextBox's Text value when it's in a data template:

    private String GetTextBoxText(int index)
            {
                // index = the index of the item in the ListView
    
                // Get the first ListViewItem in the ListView
                ListViewItem ListViewItem = (ListViewItem)weightlog.ContainerFromIndex(index);
    
                // Get the DataTemplate's StackPanel with its child elements
                StackPanel TheStackPanel = (StackPanel)ListViewItem.ContentTemplateRoot;
    
                // Get the StackPanel's child TextBox
                TextBox TheTextBox = (TextBox)TheStackPanel.Children[1];
    
                // Return the Text of the TextBox
                return TheTextBox.Text;
            }

    Just make sure the entire ListView has loaded before running this code or you'll run into null reference exceptions.

    I hope that helps!

    Friday, August 1, 2014 12:13 AM

All replies

  • I dont know if it is.

    But why would you have your textbox in itemtemplate?

    If you have an observablecollection  you can bind it with.

    <TextBox PlaceholderText="Type something.." Text={Binding Path=MyCollection.MyText, Mode=TwoWay} />

    Or if you just have a public property you can skip the "mycollection" and just add the name of the public property.

    As long as your datacontext of your page is set to see those properties you should have no problem.


    Saturday, July 19, 2014 11:43 AM
  • No this is not it. My listview itemsource is from a JSON file.
    So how can this be done then?

    Saturday, July 19, 2014 1:24 PM
  • What do you mean with "save the value I give it"?

    If you want to get a value from a textbox theres many ways to do so.

    You can use binding, then you neeed a property to bind the textbox too. Or you name your textbox something and when you press a button get that value.

    <Textbox OnClicked="myButtonClicked" />

    And then on a buttonpress you take the textboxs value and do what you want with it.

    void myButtonClicked(Object sender,
                               EventArgs e)
        {
            // We get the value from the button.
    
            Button itsClicked = (Button)sender;
    //theTExtBoxValue is whats in the textbox.
    string theTextboxValue = itsClicked.Text;
    
    }

    Sunday, July 20, 2014 7:39 AM
  • Here is what my ListView looks like, and as I said before all data is in a JSON file.

    <ListView
                        ItemsSource="{Binding Items}"
                        IsItemClickEnabled="True"
                        ContinuumNavigationTransitionInfo.ExitElementContainer="True">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Margin="0,0,0,9.5" Orientation="Horizontal">
                                    <TextBlock
                                        Text="{Binding Title}"
                                        TextWrapping="Wrap"
                                        Pivot.SlideInAnimationGroup="1"
                                        CommonNavigationTransitionInfo.IsStaggerElement="True"
                                        FontSize="25"
                                        Margin="0,0,5,0"
                                        VerticalAlignment="Center"/>
                                    <TextBox MaxLength="4" InputScope="Number" Margin="2,3,0,0" BorderThickness="0"/>
                                    <TextBlock VerticalAlignment="Center" Margin="2,3,0,0" FontSize="16" Text="Kg" Foreground="{ThemeResource PhoneAccentBrush}"/>
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
    I have 10 items in the list and if I put something in the textbox for the first item I want it to be saved for that item.
    Sunday, July 20, 2014 8:06 AM
  • I have never worked with json but I assume that you have some sort of list that is your itemsource?

    The textbox should then have a property and you bind the textbox to that property as two-way binding.

    But as I said I never worked with json so I dont know how its relationdata works. Someone else probably do.

    Sunday, July 20, 2014 9:46 AM
  • I tried to bind to the property and set the mode to two-way but the change will not take place because the json file is hardcoded I guess.

    Sunday, July 20, 2014 9:57 AM
  • Hi Alexander,

    You can certainly use data binding for this if you would like. You might have to also set the UpdateSourceTrigger to PropertyChanged, as follows:

    <TextBox MaxLength="4" InputScope="Number" Margin="2,3,0,0" BorderThickness="0" Text="{Binding Path=PropertyNameHere, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged} />

    Note that you would replace "PropertyNameHere" with the name of your property. If you want the property to be updated whenever you make a change to the TextBox, you would also have to implement the INotifyPropertyChanged interface. Here is a sample from the MSDN. And here is an example in your context:

    public class TestClass : INotifyPropertyChanged
    {
        private String text;
    
        public String Text
        {
            get { return text; }
            set
            {
                text = value;
                OnPropertyChanged("Text");
            }
        }
    
        // Declare the PropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;
    
        // Create the OnPropertyChanged method to raise the event 
        public void OnPropertyChanged(String name)
        {
            // Set the EventHandler to the PropertyChanged event
            PropertyChangedEventHandler handler = PropertyChanged;
    
            // Make sure the event exists
            if (handler != null)
            {
                // It does, so invoke the PropertyChanged event
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }

    So in this scenario, you would bind the text of the TextBox to the Text property of the TestClass class. You would then change your XAML to be as follows:

    <TextBox MaxLength="4" InputScope="Number" Margin="2,3,0,0" BorderThickness="0" Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged} />

    This way, whenever the value of your TextBox is changed, your ItemSource will be updated as well.

    I believe this is what you were asking about. Please reply if you need anything else!

    Best regards,

    Alex

    Sunday, July 20, 2014 2:30 PM
  • Hi. This looks great but I don't get it to work.
    Can I post some code that can get more help to the answer.
    Sunday, July 20, 2014 10:14 PM
  • Hi Redhoody,

    Sure thing. Do you have more than one account, because Alexander Mogren was the person who created the topic?

    Sunday, July 20, 2014 10:58 PM
  • Oops. Yes I have one more account and I was supposed to post with my other account.

    I am doing the application for wp 8.1 runtime and I don't know if the Inotifychange is made in some other way for runtime.

    I have added the TestClass to my mainpage.cs or should I make a on cs file for this?


    The JSON file is handled with a SampleDataSource.cs. As I mentation before I have the PivotPage template as default and using the DataModel that's used there.
    • Edited by Redhoody Monday, July 21, 2014 6:58 AM more text
    Monday, July 21, 2014 6:39 AM
  • why is it not working with propertychange? Is it because I develop windows phone runtime. When I use the code above it should change the property but nothing happen. Is the reason for this that I have the data in a JSON file?

    Friday, July 25, 2014 8:48 AM
  • Hi Alexander,

    Sorry for the late reply. Can you please post some more code so that it would be easier to see where the issue might be? Both XAML and C# code would be preferred.

    Thanks,

    Alex

    Saturday, July 26, 2014 1:59 PM
  • Ok.

    Here is the XAML for my ListView:

    <PivotItem Header="logg"
                           DataContext="{Binding FirstGroup}"
                           d:DataContext="{Binding Groups[0], Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"
                           CommonNavigationTransitionInfo.IsStaggerElement="True">
                    <ListView
                        ItemsSource="{Binding Items}"
                        IsItemClickEnabled="True"
                        ContinuumNavigationTransitionInfo.ExitElementContainer="True">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Margin="0,0,0,9.5" Orientation="Horizontal">
                                    <TextBlock
                                        Text="{Binding Title}"
                                        TextWrapping="Wrap"
                                        Pivot.SlideInAnimationGroup="1"
                                        CommonNavigationTransitionInfo.IsStaggerElement="True"
                                        FontSize="25"
                                        Margin="0,0,5,0"
                                        VerticalAlignment="Center"/>
                                    <TextBox MaxLength="4" InputScope="Number" Margin="2,3,0,0" BorderThickness="0" Text="{Binding Path=Subtitle,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                    <TextBlock VerticalAlignment="Center" Margin="2,3,0,0" FontSize="16" Text="Kg" Foreground="{ThemeResource PhoneAccentBrush}"/>
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </PivotItem>
            </Pivot>

    "Subtitle" is the property in my data model that's defined by SampleDataSource.cs. I have done as your example above and set the binding path to "Text" but no luck.

    The testclass is in the mainpage.cs and looks like this:

    public class TestClass : INotifyPropertyChanged
            {
                private String text;
    
                public String Text
                {
                    get { return text; }
                    set
                    {
                        text = value;
                        OnPropertyChanged("Text");
                    }
                }
    
                // Declare the PropertyChanged event
                public event PropertyChangedEventHandler PropertyChanged;
    
                // Create the OnPropertyChanged method to raise the event 
                public void OnPropertyChanged(String name)
                {
                    // Set the EventHandler to the PropertyChanged event
                    PropertyChangedEventHandler handler = PropertyChanged;
    
                    // Make sure the event exists
                    if (handler != null)
                    {
                        // It does, so invoke the PropertyChanged event
                        handler(this, new PropertyChangedEventArgs(name));
                    }
                }
            }
    What more code can be good to see to solve this?


    Monday, July 28, 2014 12:17 AM
  • Hi Alexander,

    Are you storing the JSON data in a class? The data binding may not be working because you do not have the data stored in a class. Here is an example of how you would perform data binding using the TestClass that I created. You can put this code in the Page_Loaded event just for testing purposes:

    // Create a new list of TestClass objects
    List<TestClass> TestClasses = new List<TestClass>();
    
    // Create a new TestClass to add to the list
    TestClass TheClass = new TestClass();
    
    // Set the Text property of the TestClass object
    TheClass.Text = "hello";
    
    // Add the TestClass object to the list of TestClasses
    TestClasses.Add(TheClass);
    
    // Set the ItemSource of the ListView to the TestClasses list
    ListView.ItemSource = TestClasses;

    Then you would change your TextBox in the DataTemplate to be as follows:

    <TextBox MaxLength="4" Margin="2,3,0,0" BorderThickness="0" Text="{Binding Path=Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>

    After doing this, you should notice that the TextBox is populated with "hello." If you change the contents of the TextBox, the actual TestClass object that binds to it (in this case, TheClass) will have its Text property modified as well. So if you change the text in the TextBox to be "hey," TheClass.Text will be equal to "hey."

    I'm not sure if this directly answers your question, but hopefully this provides you with some insight which you can use to apply to your own project. Please don't hesitate to reply if you have any additional questions.

    Monday, July 28, 2014 1:35 AM
  • Hi again. This does not fix my problem. What if I have all the ListViewItems manually like this: <ListViewItem>Item 1</ListViewItem> Instead of having itemsource from my JSON file. Still the changed data does not being saved.
    Monday, July 28, 2014 12:34 PM
  • Hi Alexander,

    This will not work because the ListViewItem class does not have a property that can reliably bind to the Text property of a TextBox, since the Text property is of type String. You will probably want to use a custom class as a DataSource so that you can associate a property in the custom class with the Text property of a TextBox. The TestClass example above has a Text property of type String that you can use to associate with the TextBox's Text property. For this reason, it works. You may want to consider building off of that class and storing all of your JSON data in a custom class like it so that the data binding would work.

    Would it be possible at all to upload a sample project so that I can work on resolving the issue more quickly? I apologize for taking quite a while to assist you in resolving this issue.

    Thanks,

    Alex

    Monday, July 28, 2014 11:04 PM
  • Ok. I can send the project to your email. You can also just make a project with the Pivot template because I am using that. But it maybe easier to just send you all the files
    Tuesday, July 29, 2014 7:31 AM
  • Hi Alexander,

    Sure thing. In fear of spam bots, please go to the website in my profile and send an email to support, with your project attached. I will take it from there. Alternatively, you can upload your project to OneDrive and post a download link here.

    Thanks,

    Alex

    Tuesday, July 29, 2014 11:57 AM
  • Here is a OneDrive link for the project.

    https://onedrive.live.com/redir?resid=2059D5B5B550B75C!63704&authkey=!ALjE07JBRfm1WsU&ithint=folder%2csln

    I have some more issues with this project that you maybe can help me with if you got the time.

    Thanks.

    Tuesday, July 29, 2014 6:15 PM
  • Hi Alexander,

    I apologize for the late reply. I think I got it figured out. Please try the following:

    1. Go into DataModel/SampleDataSource.cs

    2. Add the following import statement to the top:

    using System.ComponentModel;

    3. Replace your entire SampleDataItem class with the following:

    public class SampleDataItem : INotifyPropertyChanged
        {
            public SampleDataItem(String uniqueId, String title, String subtitle, String imagePath, String description, String content, String goal, String startpos, String execution, String set, String rep)
            {
                this.UniqueId = uniqueId;
                this.Title = title;
                this.Subtitle = subtitle;
                this.Description = description;
                this.ImagePath = imagePath;
                this.Content = content;
                this.Goal = goal;
                this.Startpos = startpos;
                this.Execution = execution;
                this.Set = set;
                this.Rep = rep;
            }
    
            public string UniqueId { get; private set; }
            public string Title { get; private set; }
    
            private string subtitle;
    
            public string Subtitle
            {
                get { return subtitle; }
                set
                {
                    subtitle = value;
                    OnPropertyChanged("Subtitle");
                }
            }
    
            public string Description { get; private set; }
            public string ImagePath { get; private set; }
            public string Content { get; private set; }
            public string Goal { get; private set; }
            public string Startpos { get; private set; }
            public string Execution { get; private set; }
            public string Set { get; private set; }
            public string Rep { get; private set; }
    
            public override string ToString()
            {
                return this.Title;
            }
    
            // Declare the PropertyChanged event
            public event PropertyChangedEventHandler PropertyChanged;
    
            // Create the OnPropertyChanged method to raise the event 
            public void OnPropertyChanged(String name)
            {
                // Set the EventHandler to the PropertyChanged event
                PropertyChangedEventHandler handler = PropertyChanged;
    
                // Make sure the event exists
                if (handler != null)
                {
                    // It does, so invoke the PropertyChanged event
                    handler(this, new PropertyChangedEventArgs(name));
                }
            }
        }

    All I did here was implement the INotifyPropertyChanged interface and change the Subtitle property to use the interface.

    4. Navigate to PivotPage.xaml, and find the last PivotItem ("viktlogg"). Change the PivotItem to the following:

    <PivotItem Header="viktlogg"
                           DataContext="{Binding FirstGroup}"
                           d:DataContext="{Binding Groups[0], Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"
                           CommonNavigationTransitionInfo.IsStaggerElement="True">
                    <ListView
                        ItemsSource="{Binding Items}"
                        ItemClick="weightlog_ItemClick"
                        IsItemClickEnabled="True"
                        ContinuumNavigationTransitionInfo.ExitElementContainer="True"
                        x:Name="weightlog">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Margin="0,0,0,9.5" Orientation="Horizontal">
                                    <TextBlock
                                        Text="{Binding Title}"
                                        TextWrapping="Wrap"
                                        Pivot.SlideInAnimationGroup="1"
                                        CommonNavigationTransitionInfo.IsStaggerElement="True"
                                        FontSize="25"
                                        Margin="0,0,5,0"
                                        VerticalAlignment="Center"/>
                                    <TextBox MaxLength="4" InputScope="Number" Margin="2,3,0,0" BorderThickness="0" Text="{Binding Path=Subtitle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                                    <TextBlock VerticalAlignment="Center" Margin="2,3,0,0" FontSize="16" Text="Kg" Foreground="{ThemeResource PhoneAccentBrush}"/>
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </PivotItem>

    Here, I handled the ItemClick event in the ListView, and I made the TextBox's Text property bind to the Subtitle property of the SampleDataItem object.

    5. Go into the code behind for PivotPage.xaml, and add the following code to the very bottom:

    private void weightlog_ItemClick(object sender, ItemClickEventArgs e)
            {
                SampleDataItem item = (SampleDataItem)e.ClickedItem;
            }

    Whenever you tap/click on an item in the ListView, this event will fire. You can set a breakpoint here to find out the value of the SampleDataItem's Subtitle property. You should find that if you change the text of the TextBox, the Subtitle property of the clicked SampleDataItem changes.

    I tried for about an hour to convert the data back to JSON format, but I didn't have any luck. I feel you're better off copying all of the JSON data into a custom class and then serializing the custom class with a DataContractSerializer. The JSON data is already stored in the SampleDataSource class' Groups property, so essentially, you can copy all of that data by looping through each SampleDataGroup and copying its properties into a new class. After that's done, you can get rid of the SampleDataSource class and replace it with your own class (including the XAML code). That will take quite a bit of work, but it's my recommendation.

    Feel free to reply if you have any additional questions. I hope that helps!

    Thanks,

    Alex

    Thursday, July 31, 2014 2:14 AM
  • This does not change the value. But as you say that copy all data into a custom class can be the  right thing to do. Or make a local setting for every item and store it that way.
    Thursday, July 31, 2014 8:46 AM
  • Hi Alexander,

    I believe I misunderstood what you were trying to say before. When you said "this does not change the value," I assumed you were referring to the fact that the new Subtitle value did not get saved in the JSON file. This is correct. However, the Subtitle value does get changed in memory. I thought that the value was not getting changed even in memory before.

    Doing what I said about copying all the data into a custom class would probably be your best bet. If you're looking for a more crude solution, you can get the TextBox Text values, store them in a custom class, and load them separately from the rest of your data. I figure this is at least another option if you don't want to go the first route. Here's a quick sample of how you can get a TextBox's Text value when it's in a data template:

    private String GetTextBoxText(int index)
            {
                // index = the index of the item in the ListView
    
                // Get the first ListViewItem in the ListView
                ListViewItem ListViewItem = (ListViewItem)weightlog.ContainerFromIndex(index);
    
                // Get the DataTemplate's StackPanel with its child elements
                StackPanel TheStackPanel = (StackPanel)ListViewItem.ContentTemplateRoot;
    
                // Get the StackPanel's child TextBox
                TextBox TheTextBox = (TextBox)TheStackPanel.Children[1];
    
                // Return the Text of the TextBox
                return TheTextBox.Text;
            }

    Just make sure the entire ListView has loaded before running this code or you'll run into null reference exceptions.

    I hope that helps!

    Friday, August 1, 2014 12:13 AM