Trying to bind datatable with listbox...something wrong - wpf

Please help...what am I doing wrong here? Trying to bind listbox to datatable. After debugging, i see data rows in the table but some how it is not binding to listbox.
FYI. _this is the name of my current window...
<ListBox Grid.Column="1" ItemsSource="{Binding ElementName=_this, Path=MainCategoriesTable}" HorizontalAlignment="Center" BorderBrush="Transparent" Background="Transparent" x:Name="lbMainCategories">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton Grid.Column="0" Content="{Binding Path=main_category_name}" VerticalAlignment="Center" GroupName="grpMainCategory" x:Name="rdbEnableDisable" />
<Label Grid.Column="1" Width="30" Background="Transparent" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Below is the property trying to bind with...
public DataTable MainCategoriesTable
{
get { return _dtMainCategory; }
set { _dtMainCategory = value; }
}

For XAML to set the data context tocode behind this is what works for me
DataContext="{Binding RelativeSource={RelativeSource Self}}"
in code behind
this.DataContext = this;
But I used _this like you have used it successfully.
Set Presentation.Trace = High in the all XAML binding. That is not the exact syntax but if you start with Presentation it should be obvious.
Why no binding on label.
main_category_name is a public property? I notice it is in lower case.

DataTable works like a dictionary, not like an object. It doesn't expose your columns as properties, but each DataRow exposes an indexer that can be used to get the cell value. Therefore, you need to use indexer syntax:
<RadioButton Grid.Column="0" Content="{Binding Path=[main_category_name]}" VerticalAlignment="Center" GroupName="grpMainCategory" x:Name="rdbEnableDisable" />
UPDATE
Another thing that bothers me is that your MainCategoriesTable property doesn't notify about changes. If it's changed after all Bindings have been initialized, it won't work (while DependencyProperty will, because it always notifies about changes). To make it work, your context class must implement INotifyPropertyChanged interface and your property must look like this:
public DataTable MainCategoriesTable
{
get { return _dtMainCategory; }
set
{
if(value == _dtMainCategory)
{
return;
}
_dtMainCategory = value;
var h = this.PropertyChanged;
if(h != null)
{
h(this, new PropertyChangedEventArgs("MainCategoriesTable"));
}
}
}

Related

Binding SelectedIndex property of a list to a property in different class in same/another project in Windows Phone 8?

There is something that I can't understand when I bind SelectedIndex of a list to a property. It works only if the property is part of the Codebehind of this XAML page
for example : If there we have test.xaml , test.xaml.cs and AppSettings.cs , the binding works correctly only if the SelectedIndex property is bound to a property in test.xaml.cs
After some trials , I found that if I set ItemSource and write all binding stuff in Code-behind , it works even if the property is in binder.cs !
I think is related to the order of ItemSource and SelectedItem.
The Code of the Property
private Currency sTileCurrency;
public Currency STileCurrency
{
get
{
return GetValueorDefault<Currency>("STileCurrency", null);
}
set
{
sTileCurrency = value;
if (AddOrUpdateValue("STileCurrency", value))
{
settings.Save();
}
}
}
(This won't work !)
<ListBox Name="sCurrencyLB" Margin="10,0,0,0" Width="Auto" Height="180" ItemsSource="{Binding SCurrencyList}" SelectedItem="{Binding STileCurrency, Source={StaticResource appSettings}, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Name="scountryNametb" Width="50" Text="{Binding code}" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Image Source="{Binding imgUrl}" Height="50" Width="50" HorizontalAlignment="Left" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But if we removed SelectedItem and ItemSource from XAML and set it in Codebehind
(This will work !)
sCurrencyLB.ItemsSource = Binder.Instance.SCurrencyList;
Binding binding = new Binding();
binding.Mode = BindingMode.TwoWay;
binding.Source = settings;
binding.Path = new PropertyPath("STileCurrency");
sCurrencyLB.SetBinding(ListBox.SelectedItemProperty, binding);
When I try to set ItemSource in XAML and Binding in Codebehind , it doesn't work too

Binding to a viewmodel property in a DataTemplate

I'm fairly new to XAML but enjoying learning it. The thing I'm really struggling with is binding a property to an element in a DataTemplate.
I have created a simple WPF example to, (hopefully,) explain my problem.
I this example I am trying to bind the Visibility property of a CheckBox in a DataTemplate to a Property in my viewmodel. (Using this scenario purely for learning/demo.)
I have a simple DataModel named Item, but is of little relevance in this example.
class Item : INotifyPropertyChanged
{
// Fields...
private bool _IsRequired;
private string _ItemName;
And a fairly simple View Model named ItemViewModel.
class ItemViewModel : INotifyPropertyChanged
{
private ObservableCollection<Item> _Items;
private bool _IsCheckBoxChecked;
private bool _IsCheckBoxVisible;
public ObservableCollection<Item> Items
{
get { return _Items; }
set { _Items = value; }
}
public bool IsCheckBoxChecked
{
get { return _IsCheckBoxChecked; }
set
{
if (_IsCheckBoxChecked == value)
return;
_IsCheckBoxChecked = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxChecked"));
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
}
}
public bool IsCheckBoxVisible
{
get { return !_IsCheckBoxChecked; }
set
{
if (_IsCheckBoxVisible == value)
return;
_IsCheckBoxVisible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
(Constructors and INotifyPropertyChanged implementation omitted for brevity.)
Controls laid out in MainPage.xaml as follows.
<Window.Resources>
<local:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
<Window.DataContext>
<local:ItemViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<CheckBox x:Name="checkBox" Content="Hide CheckBoxes" FontSize="14" IsChecked="{Binding IsCheckBoxChecked, Mode=TwoWay}" />
<ListView ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch" >
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemName}"/>
<CheckBox Grid.Column="1" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" >
<CheckBox.DataContext>
<local:ItemViewModel/>
</CheckBox.DataContext>
</CheckBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Horizontal" Margin="4,4,0,0">
<TextBlock Text="IsCheckBoxVisible:"/>
<TextBlock Text="{Binding IsCheckBoxVisible}" Margin="4,0,0,0" FontWeight="Bold" />
</StackPanel >
<Button Content="Button" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" Margin="4,4,4,4"/>
</StackPanel>
</Grid>
The 'Hide CheckBoxes' checkbox is bound to IsCheckBoxChecked and is used to update IsCheckBoxVisible. I've also added a couple of extra controls below the DataTemplate to prove, (to myself,) the everything works.)
I have also implemented Jeff Wilcox's value converter. (Thank you.) http://www.jeff.wilcox.name/2008/07/visibility-type-converter/
When I run the app, checking and unchecking the 'Hide Checkbox', controls outside the DataTemplate function as expected but, alas, the Checkbox inside the data template remains unchanged.
I have had success with:
IsVisible="{Binding IsChecked, Converter={StaticResource VisibilityConverter}, ElementName=checkBox}"
But I'm not just trying mimic another control but make decisions based on a value.
I would REALLY appreciate any help or advice you can offer.
Thank you.
When you are in a DataTemplate, your DataContext is the data templated object, in this case an Item. Thus, the DataContext of the CheckBox in the DataTemplate is an Item, not your ItemViewModel. You can see this by your <TextBlock Text="{Binding ItemName}"/>, which binds to a property on the Item class. The Binding to IsCheckBoxVisible is trying to find a property called IsCheckBoxVisible on Item.
There are a couple of ways around this, but by far the easiest is to do this:
On your Window (in the xaml), give it and x:Name. Eg:
<Window [...blah blah...]
x:Name="MyWindow">
Change your binding to look like this:
<CheckBox Grid.Column="1"
Visibility="{Binding DataContext.IsCheckBoxVisible, ElementName=MyWindow, Converter={StaticResource VisibilityConverter}}">
We're using the Window as the source for the Binding, then looking at its DataContext property (which should be your ItemViewModel, and then pulling off the IsCheckBoxVisible property.
Another option, if you want something fancier, is to use a proxy object to reference your DataContext. See this article on DataContextProxy.

How to bind Dictionary object to checkedlistbox using WPF

I have a generic dictionary collection Dictionary. I need to bind the displaymember path key to the content of the checkbox and checkbox Ischecked property to the value member of the Dictionary
private Dictionary<string, bool> _columnHeaderList;
public Dictionary<string, bool> ColumnHeaderList
{
get { return _columnHeaderList; }
set { _columnHeaderList = value; RaisePropertyChanged("ColumnHeaderList"); }
}
private Dictionary<string, bool> GetColumnList()
{
Dictionary<string, bool> dictColumns = new Dictionary<string, bool>();
Array columns = Enum.GetValues(typeof(ColumnHeaders));
int arrayIndex=0;
for(int i=0;i<columns.Length;i++)
{
dictColumns.Add(columns.GetValue(arrayIndex).ToString(), true);
}
return dictColumns;
}
My XAML looks like
<ListBox Grid.Column="0" Grid.Row="1" Height="200"
ItemsSource="{Binding ColumnHeaderList}"
VerticalAlignment="Top">
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox Content="{Binding key}" IsChecked="{Binding Path=Value}"></CheckBox>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You will need to use OneWay binding if you bind to Dictionary because the KeyValuePair has read-only properties.
<CheckBox Content="{Binding Key, Mode=OneWay}" IsChecked="{Binding Path=Value, Mode=OneWay}" Width="100" /></CheckBox>
make sure you have set the DataContext. Note this will not update the dictionary values when the user presses on the checkboxes.
Since Value property is readonly and OneWay binding will not allow you track the changes if user checks or unchecks the checkboxes. It is recommended to bind them with an array new class ListItem:
class ListItem
{
public string Text { get; set; }
public bool IsChecked { get; set; }
}
private ListItem[] GetColumnList()
{
return Enum.GetValues(typeof(ColumnHeaders))
.Select(h => new ListItem{ Text = h.ToString(),IsChecked = true})
.ToArray();
}
Yeah its possible and it should work too, although you need to bind with Value with Binding Mode as OneWay since the dictionary value can't be set since its readOnly. If you wish to change the value you can hook the Command(if following MVVVM) or can handle in code behind on Checked event.
Also Binding for Key is not correct, replace your keywith Key. Your final xaml should be like this -
<ListBox Grid.Column="0" Grid.Row="1" Height="200"
ItemsSource="{Binding ColumnHeaderList}"
VerticalAlignment="Top">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Key}"
IsChecked="{Binding Path=Value, Mode=OneWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note i have changed the HierarchicalDataTemplate with DataTemplate since i could not see any hierarchy in the template.

How to change TabItem indices of a TabControl at runtime?

I have five TabItem's in my TabControl and I need to move the position of each tab continuously at runtime. Can anyone tell me how can I change tab index from one position to another position at runtime.
Thanks,
#nagaraju.
Use the below solution:
TabItem tempTab = new TabItem();
tempTab = control.Items[0] as TabItem;
control.Items[0] = control.Items[1];
control.Items[1] = tempTab;
This will definitely work and you have to do from code behind.
If you are using ObservableCollection the you Just have to change the position of the Item in your collection it will be refelected in View...
For Example..
<TabControl ContentTemplate="{StaticResource ResourceKey=listView}"
ItemContainerStyle="{StaticResource ResourceKey=myTabItem}"
ItemsSource="{Binding Path=Persons}"
SelectedItem="{Binding Path=SelectedPerson}"
Style="{StaticResource ResourceKey=myTab}"
TabStripPlacement="Bottom">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="16"
Height="16"
Margin="0,0,2,0"
Source="Themes\Water lilies.jpg" />
<TextBlock Margin="0,4,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
<Button Grid.Row="1" Width="50" Command="{Binding Path=ChangePositionCommand}"> ClickMe </Button>
Here you just Change the Position of the item in TabList in ViewModel and that Position will be changed accordingly...
In Your ViewModel
I have the implementation if getting Data and Setting Up Commands... that up to you how you Do it
public ICommand ChangePositionCommand { get; private set; }
public Person SelectedPerson
{
get { return selectedPerson; }
set
{
selectedPerson = value;
InvokePropertyChanged(new PropertyChangedEventArgs("SelectedPerson"));
}
}
private void ChangePosition(object obj)
{
int index = Persons.IndexOf(SelectedPerson);
if (index <= (Persons.Count-1))
{
Persons.Move(index,index+1);
}
else
{
Persons.Move(index,0);
}
}
The above code my give INdex out of bound but I am no where near an IDE so cant test that you could reapir it according to you.
You need to change the TabControl.Items Collection. Remove the tab from the old Position and set it on a new Positon.
See How to change the order of the TabItem in the wpf TabControl

Solution for filtering all instances of an object?

I'm wondering if I can do something like this with CollectionViewSource too. I have a DataTemplate that looks like this:
<DataTemplate DataType="{x:Type local:MyObject}">
<StackPanel Orientation="Horizontal">
<Grid>
<Image Source="Images\gear16.png" />
<Image Source="Images\disk.gif" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Visibility="{Binding MyProp, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
<TextBlock Margin="5,0,0,0" Text="{Binding Name}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
So of course, everything bound to that type of object takes that DataTemplate, or in other words, every object of type MyObject gets that datasource. Can I do something similar for CollectionViewSource? Make every object of type MyObject go through the filtering methods?
The problem is that I have several instances of this collection oF MyObject, and it will be very difficult to filter one by one (I think), and still handle updates to data and everything, so I'm wondering if there is a solution like this.
Thanks!
You can use CollectionView.Filter property to perform filtering. There's no way for any "group" filtering, only "one by one" as you say. You can read here about filtering.
Your filtering handler will look like this:
private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
if (e.Item is MyObject)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
Hope it helps.

Resources