I'm using Microsoft Activity Library Designer; For some reasons I need to use ListBox to show some information in it.But I have a problem with it's ItemsSource binding.My Activity side property is like this:
private ObservableCollection<string> _selectedItems;
public ObservableCollection<string> SelectedItems
{
get
{
if (_selectedItems == null)
{
ObservableCollection<string> items = new ObservableCollection<string>();
return items;
}
return _selectedItems;
}
set
{
_selectedItems = value;
}
}
And my XAML side code is like this:
....
<Button Content="Add Item" HorizontalAlignment="Stretch" Grid.Column="0"
Click="Button_Click" Margin="5, 0, 5, 5"/>
<Button Content="Remove Item" HorizontalAlignment="Stretch" Grid.Column="1"
Click="DelButton_Click" Margin="5, 0, 5, 5"/>
....
<ListBox x:Name="LstSelectedPosts" MinHeight="20" ItemsSource="{Binding Path=ModelItem.Selecteditems, Mode=TwoWay}"/>
....
Now when I try to Add/Remove an item to/from this ListBox in Add Item and Remove Item buttons click event, debugger shows me an error that tells I can't modify the ListBox binding source.
So how can I change this Listbox's Items?
Ok there are some errors in your code that could cause the problem.
In the getter, I think you should have this.
if (_selectedItems == null)
{
_selectedItems = new ObservableCollection<string>();
}
return _selectedItems;
In your version, _selectedItems never get initialized.
In the Xaml code, when you set the ItemSource, you wrote Seleceteditems instead of SelectedItems this error doesn't cause an error when you compile but your listBox doesn't have its itemSource setted to the correct element.
And then, you didn't specify the source in
ItemsSource="{Binding Path=ModelItem.Selecteditems, Mode=TwoWay}
that means the source is by default, the DataContext of your object and that DataContext should be initialized with an object that has a public property named ModelItem which has in turn a public property named Selecteditems.
Hope it works.
Here is a small example.
in my xaml file
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Height="287" HorizontalAlignment="Left" Margin="12,12,0,0" x:Name="LstSelectedPosts" VerticalAlignment="Top" Width="294"
ItemsSource="{Binding Path=SelectedItems, Mode=TwoWay}"/>
<Button Content="Add Item" HorizontalAlignment="Stretch" Click="Button_Click" Margin="321,110,68,170"/>
<Button Content="Remove Item" HorizontalAlignment="Stretch" Click="DelButton_Click" Margin="321,147,68,129"/>
</Grid>
in my xaml.cs file
public partial class MainWindow : Window
{
private CDataContext _myCDataContext;
public MainWindow()
{
InitializeComponent();
_myCDataContext = new CDataContext();
DataContext = _myCDataContext;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_myCDataContext.Add();
}
private void DelButton_Click(object sender, RoutedEventArgs e)
{
_myCDataContext.Remove(LstSelectedPosts.SelectedItem.ToString());
}
}
and my CDataContext class
class CDataContext
{
private int _count = 0;
private ObservableCollection<string> _selectedItems;
public ObservableCollection<string> SelectedItems
{
get
{
if (_selectedItems == null)
{
_selectedItems = new ObservableCollection<string>();
}
return _selectedItems;
}
set
{
_selectedItems = value;
}
}
public void Remove(string s)
{
SelectedItems.Remove(s);
}
public void Add()
{
SelectedItems.Add(_count.ToString());
_count++;
}
}
Related
I have a collection of Objects with dependencyproperties, and Observer patern as I feed realtime data in "AssetIHM" Object.
public class assetVM: DependencyObject ,IObserver
{
public assetVM(AssetIHM aihm)
{
ObserverCollectionSingleton.ObserverCollectionInstance.Add(this);
_aihm = aihm;
}
private string _assetname;
public string assetname
{
get { return _assetname; }
set
{
_assetname = value;
SetValue(assetnameprop, value);
}
}
public static readonly DependencyProperty assetnameprop =
DependencyProperty.Register("assetnameprop", typeof(string),typeof(assetVM), new UIPropertyMetadata(""));
...
I also have a UserControl, Which should display the information contained in the AssetVM object:
public partial class AssetPanel : UserControl
{
public assetVM Asset
{
get
{
return (assetVM)GetValue(assetProperty);
}
set
{
SetValue(assetProperty, value);
}
}
public static DependencyProperty assetProperty = DependencyProperty.Register(
"Asset", typeof(assetVM), typeof(AssetPanel), new PropertyMetadata(null, new PropertyChangedCallback(OnCurrentItemChanged)));
public AssetPanel(assetVM _Asset)
{
Asset = _Asset;
this.DataContext = this;
InitializeComponent();
...
}
public AssetPanel( )
{
this.DataContext = this;
InitializeComponent();
...
}
...
I my main windows, I have a ListBox,
<Window x:Class="ETRportfolio.MainWindow"
DataContext = "{Binding RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UserControls="clr-namespace:ETRportfolio"
Title="MainWindow" Height="1200" Width="1425">
<StackPanel HorizontalAlignment="Left" Height="1153" VerticalAlignment="Top" Width="1400" Margin="10,10,-18,0">
<ListBox x:Name="Gridview" Height="800" >
<ListBox.ItemTemplate>
<DataTemplate>
<UserControls:AssetPanel Asset="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
MY problem is that I would like to feed my Usercontrol with the data contained is the collection of AssetVM.
public partial class MainWindow : Window
{
public static Dispatcher curDispatcher;
public ObservableCollection<assetVM> Datas
{
get
{
return (ObservableCollection<assetVM>)curDispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.DataBind,
(DispatcherOperationCallback)delegate { return GetValue(DataSProperty); },
DataSProperty);
}
set
{
curDispatcher.BeginInvoke(DispatcherPriority.DataBind,
(SendOrPostCallback)delegate { SetValue(DataSProperty, value); },
value);
}
}
public readonly DependencyProperty DataSProperty = DependencyProperty.Register("DataS", typeof(ObservableCollection<assetVM>), typeof(MainWindow), new PropertyMetadata(null, new PropertyChangedCallback(OnCurrentItemChanged)));
public MainWindow()
{
this.DataContext = this;
curDispatcher = this.Dispatcher;
Datas =new ObservableCollection<assetVM>();
InitializeComponent();
Gridview.ItemsSource = Datas;
addasset.addbtn.Click += onclik;
}
When the AssetPanel constructor is created, it doesn't bind with My AssetVM datas. I always pass through the empty constructor.
How Can I do that in XAML?
I guess the problem is there:
ListBox.ItemTemplate>
<DataTemplate>
<UserControls:AssetPanel Asset="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
Thks!
Edit::
I removed this.DataContext = this;
In the UserControl constructor, but the UserControl Constructor called when a DataObject
assetVM
is added to the ObservableCollection Datas, used as datasource for the Listview, is this the empty constructor. but not:
public AssetPanel(assetVM _Asset)
> {
> Asset = _Asset;
> InitializeComponent();
> ValuationInfo.ItemsSource = new List<string> { "%", "Value" };
> AVbox.ItemsSource = Enum.GetValues(typeof(AV)).Cast<AV>();
> }
So it doesn't bind.
Edit2::
private static void OnCurrentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
AssetPanel instance = (AssetPanel)d;
instance.Asset = (assetVM)e.NewValue;
return;
}
It binds ! :)
Nevertheless, the Registered Dependency properties in the dataobject are not displayed.
This is my Assetpanel xaml:
<UserControl x:Class="ETRportfolio.AssetPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="152" d:DesignWidth="1400">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border BorderBrush="#FFDE6A6A" BorderThickness="1" Grid.Row="0" Grid.Column="0" Background="lightblue">
<TextBlock x:Name="assetnamebox" TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl }, Path = assetnameprop, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="15"/>
</Border>
...
TextBlock x:Name="assetnamebox" TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl }, Path = assetnameprop,
is not binding with assetVM.assetname VIA the DependencyProperty assetnameprop.
What is wrong there?
thks
Setting a UserControl's DataContext to itself, as done in your constructors by the statements
this.DataContext = this;
effectivly disables binding to properties of inherited DataContexts, like in
<ListBox.ItemTemplate>
<DataTemplate>
<UserControls:AssetPanel Asset="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
where the binding source is the inherited DataContext of the ListBoxItems, i.e. an assetVM instance.
Remove the DataContext assignment from your UserContr's constructors.
I believe this is possible, but I don't seem to be able to get it to work; here's my view model:
namespace MyApp.ViewModel
{
public class MainViewModel : INotifyPropertyChanged
{
private static MainViewModel _mvm;
public static MainViewModel MVM()
{
if (_mvm == null)
_mvm = new MainViewModel();
return _mvm;
}
private string _imagePath = #"c:\location\image.png";
public string ImagePath
{
get { return _imagePath; }
set
{
SetProperty<string>(ref _imagePath, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged<T>(propertyName);
return true;
}
private void OnPropertyChanged<T>([CallerMemberName]string caller = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(caller));
}
}
...
Here's my App.xaml:
xmlns:vm="using:MyApp.ViewModel">
<Application.Resources>
<ResourceDictionary>
<vm:MainViewModel x:Key="MainViewModel" />
</ResourceDictionary>
</Application.Resources>
Here's the binding:
<Page
...
DataContext="{Binding MVM, Source={StaticResource MainViewModel}}">
<StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
<TextBlock FontSize="30" Margin="10">Image</TextBlock>
<TextBox Text="{Binding ImagePath}" Margin="10"/>
</StackPanel>
...
I don't seem to be able to get the binding to work; what have I missed here? I would expect the field to be populated with the default value, but it isn't; I've put breakpoints in the ViewModel, but it is not breaking.
To me your binding syntax is incorrect. DataContext="{Binding MVM, Source={StaticResource MainViewModel} means you should have a "MVM" PROPERTY in your MainViewModel class. In your case MVM is a method.
Try replacing your MVM method by a property. That might work.
Another way to do it, is to set
DataContext="{StaticResource MainViewModel}"
In that case, the MVM method will be obsolete (I did not try it on WinRT)
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.
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 );
}
}