In my WP8 app I have a class, which has a ObservableCollection<ObservableCollection<int>> property called Matrix.
I want to display these matrices using items control.
<ItemsControl ItemsSource="{Binding FirstMatrix.Matrix}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The code works as far as displaying is concerned (it's filled with zeros which is a default value). But I also want to allow changes in TextBoxes which would be reflected in Matrix property - now the TextBoxes can't be changed, because their value is bound one way to Matrix cells I guess. I tried setting <TextBox Text="{Binding Mode=TwoWay}" />or sth similar but it doesn't seem to work.
Any ideas how should the data be bound ?
EDIT:
I have implemented the INotifyPropertyChanged.
Here is a part of my class:
public partial class CalcMatrix : INotifyPropertyChanged
{
public ObservableCollection<ObservableCollection<int>> Matrix
{
get { return _matrix; }
set
{
_matrix = value;
OnPropertyChanged("Matrix");
}
}
private ObservableCollection<ObservableCollection<int>> _matrix;
private void OnPropertyChanged(string argName)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(argName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I think the reason the TexBoxes don't change is because the binding is one-way - the Text is always what is inside the Matrix. I believe that i should somehow change the XAML binding to TwoWay or something but don't know how. Any ideas ?
Two way mode binding require path (why? see this SO answer), so you can't do it just like {Binding Mode=TwoWay}, it has to be something like {Binding SomePath, Mode=TwoWay}. Therefore, in this case you have to wrap matrix item to be some class instead of plain int and put that int as property's value of that class.
//your Matrix property type become:
...
public ObservableCollection<ObservableCollection<MatrixElement>> Matrix
...
//MatrixElement class is something like:
public class MatrixElement : INotifyPropertyChanged
{
private int _value;
public int Value
{
get { return _value; }
set {
_value = value;
OnPropertyChanged("Value");
}
}
....
}
//then you can bind TextBox in two way mode
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, Mode=TwoWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
...
The reason it didnt work is that the itemsource is a list of Matrix and you are not making any changes to the list iteself like adding or removing from a list instead you are changing a property of the item present in list I assume you are using an ObservableCollection....
So you need to implement a INotifyPropertyChanged interface to tell the UI that Hey I am changed please update yourself....
class YourClass : INotifyPropertyChanged
{
private string yourProperty;
public string YourPropety{
get{
return yourProperty;
}
set{
if (value != this.yourProperty)
{
this.yourProperty = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Related
I have a listview in uwp , and a view model that declare pataient_List and selected_patient in this. my listview show itemsource but I don't know why my listview do not show selected item.
<ListView ItemsSource="{Binding pataient_List}"
SelectedItem="{Binding selected_patient, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding name_to_show_menu, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
viewmodel is
public class patient_view_model : notify_property_changed_base
{
public patient_view_model(patient patient_param)
{
pataient_List = new ObservableCollection<patient>();
load_patient(); // this function put patients in pataient_List
selected_patient = patient_param;
}
public patient selected_patient
{
get { return _selected_patient; }
set
{
if (_selected_patient != value)
{
_selected_patient = value;
RasiePropertyChanged();
}
}
}
public ObservableCollection<patient> pataient_List { set; get; }
One cause could be that the selected item must be one of the objects in the pataient_List.
Another cause is perhaps because you're setting the selected_patient in the constructor of the view model which is definitely before you bind the view model to the view. So, why not trying to set the selected_patient after you bind the view model to the view.
Forget about the ItemTemplate in the ListView.
<ListView ItemsSource="{Binding pataient_List}"
SelectedItem="{Binding selected_patient, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Foreground="Black">
</ListView>
.NET doesn't have a clue about how you want your data to be displayed, so it just calls the ToString() method on each object and uses that to represent the item. Override the ToString() method in the patient object to display what you need. Here is the code:
public class patient
{
public string name_to_show_menu;
public override string ToString()
{
return this.name_to_show_menu;
}
}
I solve issue with this one answer.
public override bool Equals(object obj)
{
if (this.name_to_show_menu == (obj as patient).name_to_show_menu)
return true;
else
return false;
}
I have been trying to get texts from the collection of textboxes which are created dynamically by binding a collection to a stackpanel using items control which is in seperate user control which i am loading on a page in windows phone runtime.
Below is the code of my UserControl:
<UserControl
x:Class="CfMobility.UserControls.CredentialsUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CfMobility.UserControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<ScrollViewer>
<ItemsControl ItemsSource="{Binding SelectedCategorySettings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel IsTapEnabled="False">
<TextBlock Text="{Binding SettingKey}" Style="{ThemeResource BaseTextBlockStyle}"></TextBlock>
<TextBox Text="{Binding SettingValue}" Width="300px"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>
I have a Content control in another page as below:
<ContentControl x:Name="Container" Grid.Row="1" Margin="19,10,19,0">
</ContentControl>
Where i am binding this content control to above stackpanel when i am navigating to the page.
As You can see i had bonded "SelectedCategorySettings" collection to StackPanel using ItemsScroll which displays number of text boxes based on the collecion. Here i am unable to figure out if i want to save text from all the text boxes which are displayed on the page into a json file, how to access the text of all the text boxes which are dynamically displayed in above scenario?
PS: note that items is control is in a separate user control.
Thanks in Advance
You should use ObservableCollection for SelectedCategorySettings and your model class which contains SettingKey and SettingValue should implement INotifyPropertyChanged interface as in following example code. If you do it correctly then whatever the changes happen in UI (in your case, text changes of textbox) will automatically reflect in your model objects in the ObservableCollection. If you are interested to learn more about how this works, I recommend you to search about mvvm design pattern in Windows Phone development.
public class Setting : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _settingKey;
public string SettingKey
{
get { return _settingKey; }
set {
_settingKey = value;
OnPropertyChanged("SettingKey");
}
}
private string _settingValue;
public string SettingValue
{
get { return _settingValue; }
set {
_settingValue = value;
OnPropertyChanged("SettingValue");
}
}
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What you need is a method like this:
var textBoxes = AllTextBoxes(this);
public List<TextBox> AllTextBoxes(DependencyObject parent)
{
var list = new List<TextBox>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is TextBox)
{
list.Add(child as Control);
continue;
}
list.AddRange(AllChildren(child));
}
return list;
}
I develop an application, and faced a problem with following part.
I have a 'textblock' that binds exact item in observable collection of items('Items') and shows the number. Each item has a property, called 'Count'.
When user taps current textblock, which is in stackpanel of longlistselector (binding Items), I need this textblock to show new number(idea is each time user taps textblock, number increases by 1).
The only method I discovered is each time Navigate to current pivot item(where textblock is situated) to force pivot item's reload data from observable collection, where updated items(and 'Count' property) are stored. But it is slow and doesn't work good.
Please, advice how to make the number appear in the textblock each time user taps on current textblock.
<phone:PivotItem Name="Documents" Header="Documents" Margin="0" >
<Grid>
<phone:LongListSelector ItemsSource="{Binding Items}" >
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
Height="95" >
<TextBlock Tap="Plus_Tap" Visibility="Visible" Width="20" Height="50" Text="{Binding Count}" FontFamily="Segoe WP Light"
FontSize="35" Foreground="White" Margin="2,0,0,2"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</phone:PivotItem>
namespace PackMan
{
public partial class CategoryList : PhoneApplicationPage
{
public CategoryList()
{
InitializeComponent();
DataContext = App.ViewModel;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
private void Plus_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
PackList selectedList = (sender as TextBlock).DataContext as PackList;
selectedList.Count += 1;
NavigationService.Navigate(new Uri("/CategoryList.xaml?Documents", UriKind.Relative));
}
}
}
How to refresh the data in pivot item after updating data through xaml UI?
here a guy in the very end of his posted answer to his own question wrote: "If you are using the observableCollection than no need to refresh the page. It is the magic of ViewModel." But I load data from 'Items' observable collection, which is not in ViewModel, but declared in Packlist.cs. Maybe this will help.
Here I do increase Count property of the item in ObservableCollection "Items" :
private void Plus_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
PackList selectedList = (sender as TextBlock).DataContext as PackList;
selectedList.Count += 1;
I tap on textblock, and text of the textblock shows no changes. I tap return - which brings me back to menu page, than again I open pivot item page, and see the textblock's text(number) increases by one. Change is seen only when I reload page(pivot item), since I have this in its constructor and OnNavigatedTo method:
public CategoryList()
{
InitializeComponent();
DataContext = App.ViewModel;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
LoadData() - loads OBservable collection ("Items"), which has items, and Count as one of its properties:
public void LoadData()
{
this.Items = LoadNewLists();
this.IsDataLoaded = true;
}
Maybe now you could tell me how to make text of the textblock change instantly as I tap on it(textblock). I will be thankful for any advice, since this moment has stopped me from developing my app second day now.
I figured it out. I should have read about INotifyPropertyChanged Interface and created 'Count' property using this interface's method:
public int Count
{
get
{
return _count;
}
set
{
if (value != _count)
{
_count = value;
NotifyPropertyChanged("Count");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Hope it will be helpful to anyone.
I have a Panorama that has ItemSource set to an ObservableCollection which is created in the viewmodel
In the ViewModel.cs
public ObservableCollection<UserSchema> SeriesData;
private string _HeaderTitle;
public string HeaderTitle
{
get
{
return _HeaderTitle;
}
private set
{
_HeaderTitle = value;
NotifyPropertyChanged("HeaderTitle");
}
}
The value from HeaderTitle comes from HeaderSchema class's Title property (see below). This is set elsewhere in the viewmodel constructor not shown here.
UserSchema.cs contains the UserSchema which has implemented INotifyProperyChanged and has two properties. I have implemented the public getter and setter
private string _objectname;
private string _objectpath;
public string ObjectName
{
get
{
return _objectName;
}
set
{
_objectName = value;
NotifyPropertyChanged("ObjectName");
}
}
Similar code for _objectPath property also.
I have another HeaderSchema.cs that has also implemented INotifyProperyChanged and has two properties. I have implemented the public getter and setter
private string _Title;
public string Title
{
get
{
return _Title;
}
set
{
_Title = value;
NotifyPropertyChanged("Title");
}
}
Once again I have created a public get and set property for above as Title
Now in MainPage.xaml I have set the Panaroma ItemSource to SeriesData, elsewhere in the code behind I have set the DataContext to the viewmodel.
<controls:Panorama x:Name="SerPan" ItemsSource="{Binding SeriesData}">
<controls:Panorama.HeaderTemplate>
<DataTemplate>
<Grid x:Name="grid">
<TextBlock Text="{Binding ObjectName}">
</TextBlock>
</Grid>
</DataTemplate>
</controls:Panorama.HeaderTemplate>
<controls:Panorama.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ObjectPath}"/>
</DataTemplate>
</controls:Panorama.ItemTemplate>
</controls:Panorama>
This works very well and I get the correct output.
What I would like is to set a Title to the Panorama and I want to get the text from HeaderTitle property from viewmodel.
Something like the below, but the problem is HeaderTitle is a different property and not part of SeriesData which is the ItemSource for panorama.
<controls:Panorama.TitleTemplate>
<DataTemplate>
<TextBlock Text="{Binding HeaderTitle}"> //does not work.
</TextBlock>
</DataTemplate>
</controls:Panorama.TitleTemplate>
If your ViewModel is the DataContext of your Panorama control you can access it via ElementName Binding:
SerPan.DataContext = myViewModelInstance;
<phone:Panorama.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementName=SerPan, Path=DataContext.HeaderTitle}" />
</DataTemplate>
</phone:Panorama.HeaderTemplate>
I have a strange problem in my WinRT/C# XAML Metro app, using the Windows 8 Release Preview (latest patches installed). I'm using a ComboBox, whose values ItemsSource and SelectedValue are bound to properties in a ViewModel:
<ComboBox SelectedValue="{Binding MySelectedValue, Mode=TwoWay}"
ItemsSource="{Binding MyItemsSource, Mode=OneWay}"
Width="200" Height="30" />
Code behind:
public MainPage()
{
this.InitializeComponent();
DataContext = new TestViewModel();
}
And a very simple definition of the TestViewModel, using strings:
public class TestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private IEnumerable<string> _myItemsSource = new List<string>
{
"Test Item 1",
"Test Item 2",
"Test Item 3"
};
public IEnumerable<string> MyItemsSource
{
get { return _myItemsSource; }
}
private string _mySelectedValue = "Test Item 2";
public string MySelectedValue
{
get { return _mySelectedValue; }
set
{
_mySelectedValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MySelectedValue"));
}
}
}
}
Now I thought this simple solution should just work... But when I start the app, the SelectedValue="Test Item 2" doesn't show up, the ComboBox is left empty. By setting breakpoints I noticed that the bound values MyItemsSource and MySelectedValue are corectly retrieved from the View Model when I set the DataContext of the view. After this action, the ComboBox.SelectedValue property is actually set to "Test Item 2", but it just doesn't show! Also I noticed that when I change the selected value in the ComboBox by user action on the UI, the changed value shows up in the ComboBox and the View Model property is updated accordingly. So everything seems to work fine except the initial visualization of the MySelectedValue View Model property. I'm becoming really desperate about that...
Now while this is the simplest example, in the origin I wanted to bind whole entities to ComboBox, setting DisplayMemberPath and SelectedValuePath. Unfortunately, the same problem occurs.
I found the problem in my example: In the XAML markup I've defined the SelectedValue property before the ItemsSource property. If I swap both definitions in this way, it works:
<ComboBox ItemsSource="{Binding MyItemsSource, Mode=OneWay}"
SelectedValue="{Binding MySelectedValue, Mode=TwoWay}"
Width="200" Height="30" />
This is really odd and annoying. Now I would like to know: is this a bug or by design? I think this is a bug, because the control should be working regardless of the order of the defined properties in XAML.
this is working solution : you can find here https://skydrive.live.com/?cid=b55690d11b67401d&resid=B55690D11B67401D!209&id=B55690D11B67401D!209
<ComboBox Width="300" Height="32" HorizontalAlignment="Left" DisplayMemberPath="Name"
VerticalAlignment="Top" ItemsSource="{Binding PersonCollection}"
SelectedItem="{Binding SelectedPerson, Mode=TwoWay}"></ComboBox>
ViewModle class is
public class ViewModel:BaseViewModel
{
private Person selectedPerson;
public Person SelectedPerson {
get { return this.selectedPerson; }
set { this.selectedPerson = value;
this.RaisePropertyChanged("SelectedPerson");
}
}
public ObservableCollection<Person> PersonCollection { get; set; }
public ViewModel()
{
this.PersonCollection = new ObservableCollection<Person>();
this.PopulateCollection();
//setting first item as default one
this.SelectedPerson = this.PersonCollection.FirstOrDefault();
}
private void PopulateCollection()
{
this.PersonCollection.Add(new Person { Name="Oscar", Email="oscar#sl.net" });
this.PersonCollection.Add(new Person { Name = "Jay", Email = "jay#sl.net" });
this.PersonCollection.Add(new Person { Name = "Viral", Email = "viral#sl.net" });
}
}