When binding data to combobox, 3 items need to be set:
<ComboBox ItemsSource="{Binding MyList}" SelectedItem="{Binding MyItem}" DisplayMemberPath="MyName" />
Say ItemSource is list of Country
I set itemsource to right source firstly, it is fine. Then I set selectedItem to specific Country object, but it's not working.
Looks like all need to be set when ItemSource is setting.
How to resolve this problem?
UPDATE WITH WORKING CODE
Make sure you enable two-way binding on the SelectedItem.
<ComboBox ItemsSource="{Binding Path=Countries, Mode=OneWay}" SelectedItem="{Binding Path=SelectedCountry, Mode=TwoWay}" Height="23" HorizontalAlignment="Left" Margin="12,28,0,0" Name="comboBox1" VerticalAlignment="Top" Width="267" />
Here is what your context will look like:
public partial class MainPage : UserControl, INotifyPropertyChanged
{
public MainPage()
{
InitializeComponent();
this.Countries = new ObservableCollection<string> { "USA", "CAN" };
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> Countries { get; set; }
private string _selectedCountry = null;
public string SelectedCountry
{
get { return _selectedCountry; }
set
{
_selectedCountry = value;
if( this.PropertyChanged != null )
this.PropertyChanged( this, new PropertyChangedEventArgs( "SelectedCountry" ) );
}
}
private void button1_Click( object sender, RoutedEventArgs e )
{
MessageBox.Show( "Selected Country: " + this.SelectedCountry );
}
}
Related
I have this simple ListView filled from an ObservableCollection. Once the list is bound, I would like to access the parent vm view model from inside this ItemTemplate so that I can bind the command called cmd_delete_mesh. How is this done for a UWP Xaml app (not wpf)?
<ListView x:Name="mesh_list" SelectedItem="{x:Bind vm.selected_mesh, Mode=TwoWay}" ItemsSource="{x:Bind vm.meshes}">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem>
<Button Command="{Binding cmd_delete_mesh}"/>
You can do like so:
<ListView x:Name="mesh_list" SelectedItem="{x:Bind vm.selected_mesh, Mode=TwoWay}" ItemsSource="{x:Bind vm.meshes}">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem>
<Button Command="{Binding ElementName=mesh_list, Path=DataContext.vm.cmd_delete_mesh}"/>
I do this from code unfortunately... I’ll post an example of my code soon
You could define your command in your model and declare an event in it. In your ViewModel, when you initialize the 'meshes' collection, you could register this event for every item in this collection. Then, when the command is executed, you just need to raise the event and do some operations in its event handler.
I made a simple code sample for your reference:
<ListView x:Name="mesh_list" SelectedItem="{x:Bind ViewModel.selected_mesh, Mode=TwoWay}" ItemsSource="{x:Bind ViewModel.meshes,Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:SubTest">
<ListViewItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind Name }"></TextBlock>
<Button Command="{x:Bind cmd_delete_mesh}" Content="delete"/>
</StackPanel>
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
ViewModel = new Test("test data");
}
private Test ViewModel { get; set; }
}
public class Test : ViewModelBase
{
public string Name { get; set; }
private SubTest _selected_mesh;
public SubTest selected_mesh
{
get { return _selected_mesh; }
set
{
if (_selected_mesh != value)
{
_selected_mesh = value;
RaisePropertyChanged("selected_mesh");
}
}
}
public ObservableCollection<SubTest> meshes { get; set; } = new ObservableCollection<SubTest>();
public Test(string name)
{
this.Name = name;
for (int i = 0; i < 10; i++)
{
var sub = new SubTest() { Name = "String " + i };
sub.DeleteParentItem += Test_DeleteParentItem;
meshes.Add(sub);
}
}
private void Test_DeleteParentItem()
{
if (selected_mesh != null)
{
DeleteItem(selected_mesh);
}
}
private void DeleteItem(SubTest subTest)
{
//TODO...
}
}
public class SubTest
{
public RelayCommand cmd_delete_mesh { get; set; }
public string Name { get; set; }
public event Action DeleteParentItem;
public SubTest()
{
cmd_delete_mesh = new RelayCommand(DeleteItem);
}
private void DeleteItem()
{
if (DeleteParentItem != null)
{
DeleteParentItem.Invoke();
}
}
}
Please note that the ViewModelBase and RelayCommand are from mvvmlight.
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
We have used DataGridTemplateColumn for our grid to display texbox under each column. We've a requirement to make the textboxes readonly if it contains any data (for data loading case). In order to achieve that, we need to access all text box controls under the radgrid. We've tried following approaches so far
Find all child controls using VisualTreeHelper - No textbox control was found
Tried with DataBindingComplete event
Is there any way to access the underlying cell's control from codebehind for RadDataGrid?
Alternative approach : Can we somehow user IsReadOnly property with some binding to check it's value and make the control readonly when value is there?
Can we somehow user IsReadOnly property with some binding to check it's value and make the control readonly when value is there?
Yes. You could certainly achieve this by using Binding. You just need to define a bool property and bind the IsReadOnly property of TextBox to this property. Then, you could change this bool value according to the text of TextBox.
Please refer to my following code sample for reference:
<telerikGrid:RadDataGrid x:Name="grid" AutoGenerateColumns="False" VerticalAlignment="Center">
<telerikGrid:RadDataGrid.Columns>
<telerikGrid:DataGridTemplateColumn Header="Country">
<telerikGrid:DataGridTemplateColumn.CellContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Country}" HorizontalAlignment="Center" VerticalAlignment="Center" IsReadOnly="{Binding IsReadOnly}"/>
</DataTemplate>
</telerikGrid:DataGridTemplateColumn.CellContentTemplate>
</telerikGrid:DataGridTemplateColumn>
<telerikGrid:DataGridTemplateColumn Header="Flag">
<telerikGrid:DataGridTemplateColumn.CellContentTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Flag}" />
</StackPanel>
</DataTemplate>
</telerikGrid:DataGridTemplateColumn.CellContentTemplate>
</telerikGrid:DataGridTemplateColumn>
</telerikGrid:RadDataGrid.Columns>
</telerikGrid:RadDataGrid>
public sealed partial class MainPage : Page
{
ObservableCollection<Data> list = new ObservableCollection<Data>();
public MainPage()
{
this.InitializeComponent();
list.Add(new Data { Country = "Argentina",Flag="A"});
list.Add(new Data {Country=string.Empty,Flag="B"});
list.Add(new Data { Country = "China",Flag="C"});
this.grid.ItemsSource = list;
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(5000);
list[1].Country = "Brazil";
}
}
public class Data:INotifyPropertyChanged
{
private string _Country;
public string Country
{
get { return _Country; }
set
{
_Country = value;
if (string.IsNullOrEmpty(value))
{
IsReadOnly = true;
}
else
{
IsReadOnly = false;
}
RaisePropertyChanged("Country");
}
}
private string _Flag;
public string Flag
{
get { return _Flag;}
set
{
_Flag = value;
RaisePropertyChanged("Flag");
}
}
private bool _IsReadOnly=false;
public bool IsReadOnly
{
get { return _IsReadOnly; }
set
{
_IsReadOnly = value;
RaisePropertyChanged("IsReadOnly");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropertyName));
}
}
}
So I'm trying to implement a ListBoxItem that has a TextBlock that binds to a property in the viewmodel. But for some reason the view isn't being updated even after the viewmodel property is updated. Can anyone give me some pointers?
In the view (xaml)
<phone:Pivot HorizontalAlignment="Left" Height="748" Margin="10,10,0,0" Grid.Row="1" Title="pivot" VerticalAlignment="Top" Width="460">
<StackPanel x:Name="POIStackPanel" DataContext="{StaticResource POIViewModel}">
<ListBoxItem x:Name="SelectedPOIItem">
<TextBlock Text="{Binding SelectedPickupPOI.label}" />
</ListBoxItem>
phone:PivotItem x:Name="GooglePivot" Header="Google">
<ListBox x:Name="GooglePOIList"
ItemsSource="{Binding GooglePOICollection}"
HorizontalAlignment="Left"
Height="441"
Width="436"
SelectedItem="{Binding SelectedPickupPOI, Mode=TwoWay}"
ItemTemplate="{StaticResource POIItemTemplate}" />
</phone:PivotItem>
</StackPanel>
</phone:Pivot>
In the viewmodel
public ObservableCollection<PointOfInterest> GooglePOICollection { get; private set; }
public PointOfInterest SelectedPickupPOI
{
get
{
return _selectedPickupPOI;
}
set
{
_selectedPickupPOI = value;
NotifyPropertyChanged("SelectedPickupPOI");
}
}
public PointOfInterestViewModel()
{
this.GooglePOICollection = = new ObservableCollection<PointOfInterest>();
this.SelectedPickupPOI = new PointOfInterest();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
My PointOfInterest Models
public abstract class PointOfInterest : INotifyPropertyChanged
{
public abstract string id { get; set; }
public abstract string label { get; set; }
}
public class GooglePOI : PointOfInterest
{
private string _label;
public override string label
{
get { return _label; }
set
{
_label = value;
NotifyPropertyChanged("label");
}
}
}
Update
so I found out that if I do _selectedPickupPOI.label = value.label; it will update the view correctly. What am I doing wrong?
I'm trying to populate a list view control on a XAML page in a Win8 application. I've added the following attributes to the page XAML:
<common:LayoutAwarePage x:Class="SecAviTools.ViewWeatherHome"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="using:MyNameSpace.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:MyNameSpace"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="using:MyNameSpace.Win8ViewModel"
x:Name="pageRoot"
DataContext="{Binding DefaultViewModel,
RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<!-- ... -->
<ListView ItemsSource="{Binding Path=viewmodel:Stations}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Id}"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My source class is:
namespace MyNameSpace.Win8ViewModel
{
public class Stations : INotifyPropertyChanged, INotifyCollectionChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged<T>(NotifyCollectionChangedAction action, ObservableCollection<T> items)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items));
}
public Stations()
{
AllStations = new ObservableCollection<Station>();
AddStations(new List<Station>());
}
public ObservableCollection<Station> AllStations { get; private set; }
public void AddStations(List<Station> stations)
{
AllStations.Clear();
foreach (var station in stations)
AllStations.Add(station);
OnCollectionChanged(NotifyCollectionChangedAction.Reset, AllStations);
OnPropertyChanged("AllStations");
}
}
public class Station
{
public int Id { get; set; }
public string Name { get; set; }
}
}
There is also a button on the page (not shown here) that does the following:
public sealed partial class MyPage : MyNameSpace.Common.LayoutAwarePage
{
private Stations m_Stations = new Stations();
//...
private async void SearchButtonClick(object sender, RoutedEventArgs e)
{
var list = new List<Station>();
list.Add(new Station() { Id = 0, Name = "Zero" });
list.Add(new Station() { Id = 1, Name = "One" });
m_Stations.AddStations(list);
}
}
However, when I run the code, nothing appears in the list view. What am I missing?
TIA
You don't show what DefaultViewModel is, but I'll assume it's set to be an instance of the class you show, Stations. In that case, you need to binding to be:
<ListView ItemsSource="{Binding Path=AllStations}">
The Path of a binding usually refers to a property somewhere; with no further specification, such as a Source, it's a property on the object that is set to be the DataContext.
Regardless, you don't need the namespace qualifier "viewmodel:".
As an aside, if you do end up binding to the ObservableCollection, you don't need to implement INotifyCollectionChanged, only INotifyPropertyChanged for when the AllStations property is set.
I need to bind all the check boxes(IsChecked Property) in an StackPannel with a bool value which is defined in a class. Attaching my Xaml with my question, please help
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Height="287" HorizontalAlignment="Left" Margin="78,65,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="309" DataContext="checkFlag">
<CheckBox Content="" Height="71" Name="checkBox1" IsChecked="{Binding Path=Binding.MainPage,Source=checkFlag,Mode=TwoWay}"/>
<CheckBox Content="" Height="71" Name="checkBox2" IsChecked="{Binding Path=Binding.MainPage,Source=checkFlag,Mode=TwoWay}"/>
</StackPanel>
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="78,400,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" />
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="227,400,0,0" Name="button2" VerticalAlignment="Top" Width="160" Click="button2_Click" />
The checkboxes are not getting check/uncheck on set/reset of checkFlag. Should i implement "INotifyPropertyChanged" for the flag Or something else. Adding the class behind also, Please have a look.'
namespace Binding
{
public partial class MainPage : PhoneApplicationPage
{
public bool checkFlag;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
checkFlag = true;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
checkFlag = false;
}
}
it looks to me like you need to implement, as you guess, INotifyPropertyChanged here is a link to the MSDN documentation
here is some demo code....
public class MyData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChangedEventArgs args = new PropertyChangedEventArgs(property);
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
int _someInt = 0;
public int SomeInt
{
get { return _someInt ;}
set { if ( value == _someInt ) return;
_someInt = value;
RaisePropertyChanged("SomeInt");
}
}
string _someString = string.Empty;
public string SomeString
{
get { return _someString ;}
set { if ( value == _someString ) return;
_someString = value;
RaisePropertyChanged("SomeString ");
}
}
}
you set up your private variables, then add a property for each one you want to expose.
in the getter, just return the value... in the setter, check if the value has changed and if so assign the value and raise the property changed event. make sure that the name you are passing in as a string is the same as the property... same capitalization, same spelling, etc.