Have a databound WPF Listbox generate subclassed ListboxItems - wpf

I would like to have my WPF Listbox, which is databound, generate subclassed ListboxItems instead of the regular ListboxItems. In this case, a DataTemplate is not sufficient because I need some custom properties for the subclassed ListBoxItems.
Is there a way to have the ListBox generated mySubClassedListBoxItem items for the bound data?
Thanks,
Bart

You need to create your own subclass of ListBox so you can override the method which creates the container, e.g.
public class MyListBox : ListBox
{
public MyListBox()
{
// Should get the default style & template since styles are not inherited
Style = FindResource(typeof(ListBox)) as Style;
}
protected override DependencyObject GetContainerForItemOverride()
{
var container = new MyListBoxItem();
return container;
}
}
public class MyListBoxItem : ListBoxItem
{
public MyListBoxItem()
{
Style = FindResource(typeof(ListBoxItem)) as Style;
// To easily see that these are custom ListBoxItems:
// TextElement.SetForeground(this, Brushes.Red);
}
// ...
}

Related

WPF Problems with Custom Control

I have a Custom Control in WPF
public class MyClass: Control, INotifyPropertyChanged
{
private Boolean _hasData;
public Boolean HasData
{
get { return _hasData};
set
{
_hasData = value;
OnPropertyChanged("HasData");
this.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
}
}
#region INotifyPropertyChanged members
// code
#endregion
}
Now here's the thing: should i use a Control Template or a Data Template?
purpose of custom control: showing data that i recieved from a service.
I tried a Custom Template, but the properties of the control aren't bound/connected with the properties of XAML code. The DataContext of my Control Template is the control itself (MyClass).
<ControlTemplate TargetType="{x:Type controls:MyClass}">
<Grid Visibility="{Binding Visibility, UpdateSourceTrigger=PropertyChanged}"}">
<TextBlock Text="Contains Data"/>
</Grid>
</ControlTemplate>
If i check the DataContext (which is the Myclass class), the visibility is Visible or Collapsed. the Visibility of the control (Myclass XAML) won't bind to the DataContext Visibility.
Also, if i set the visibility in the constructor to Collapsed, then it remains being on Collapsed. i also tried triggers and an extra Boolean property Show that is binded to the Grid Visibility (with a converter of course).
What should i do now? i just want that some Control properties like Visibility in MyClass Control have the same value as class MyClass.

Can't bind to IsExpanded on Expander

Blacklight is an older set of WPF controls and styles. The code can be found here. It contains a control called AnimatedExpander which isn't really an expander, rather it just implements HeaderedContentControl and adds an IsExpandedProperty dprop:
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register("IsExpanded", typeof(bool), typeof(AnimatedExpander), new PropertyMetadata(true));
public bool IsExpanded
{
get
{
if (this.expandToggleButton != null)
{
return this.expandToggleButton.IsChecked.Value;
}
return (bool)GetValue(IsExpandedProperty);
}
set
{
SetValue(IsExpandedProperty, value);
}
}
I need to bind to IsExpanded so that I can persist whether expanders are expanded. I'm pretty sure I have the binding setup correctly, and that there is a problem with this custom dependency property. If I open the view in Snoop, and set the IsExpanded=True on the expander, the binding works. However, just clicking the expandToggleButton on the control only expands the control, it doesn't hit my binding.
<controls:AnimatedExpander IsExpanded="{Binding SGGExpanderExpanded}" />
private bool _sGGExpanderExpanded;
public bool SGGExpanderExpanded
{
get { return _sGGExpanderExpanded; }
set
{
if (_sGGExpanderExpanded != value)
{
_sGGExpanderExpanded = value;
OnPropertyChanged("SGGExpanderExpanded");
}
}
}
How can I bind to a value that changes when the user clicks the toggle button that is wired to expand the control?
A bad solution:
I was able to make this work by attaching an event to the ToggleButton click and looking at the "sender" Content and IsChecked values to update my viewmodel.

WPF MVVM Bind list on custom control to ViewModel

Is it possible to bind data in the "wrong" direction? I want a value in a custom control to be bound to my ViewModel. I've tried binding with mode "OneWayToSource" but I can't get it to work.
Scenario (simplified):
I have a custom control (MyCustomControl) that has a dependency property that is a list of strings:
public class MyCustomControl : Control
{
static MyCustomControl()
{
//Make sure the template in Themes/Generic.xaml is used.
DefaultStyleKeyProperty.OverrideMetadata(typeof (MyCustomControl), new FrameworkPropertyMetadata(typeof (MyCustomControl)));
//Create/Register the dependency properties.
CheckedItemsProperty = DependencyProperty.Register("MyStringList", typeof (List<string>), typeof (MyCustomControl), new FrameworkPropertyMetadata(new List<string>()));
}
public List<string> MyStringList
{
get
{
return (List<string>)GetValue(MyCustomControl.MyStringListProperty);
}
set
{
var oldValue = (List<string>)GetValue(MyCustomControl.MyStringListProperty);
var newValue = value;
SetValue(MyCustomControl.MyStringListProperty, newValue);
OnPropertyChanged(new DependencyPropertyChangedEventArgs(MyCustomControl.MyStringListProperty, oldValue, newValue));
}
}
public static readonly DependencyProperty MyStringListProperty;
}
The control also contains code to manipulate this list.
I use this custom control in a UserControl that has a ViewModel. The ViewModel has a property that is also a list of strings:
public List<string> MyStringsInTheViewModel
{
get
{
return _myStringsInTheViewModel;
}
set
{
if (value != _myStringsInTheViewModel)
{
_myStringsInTheViewModel = value;
OnPropertyChanged("MyStringsInTheViewModel");
}
}
}
private List<string> _myStringsInTheViewModel;
Now I want to bind the list in my custom control (MyStringList) to the list in my ViewModel (MyStringsInTheViewModel) so that when the list is changed in the custom control it is also changed in the ViewModel. I've tried this but can't get it to work...
<myns:MyCustomControl MyStringList="{Binding Path=MyStringsInTheViewModel, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}">
How can I make such a binding?
Use ObservableCollection<T> instead of List<T>. It implements INotifyCollectionChanged Interface.

WPF ObservableCollection in xaml

I have created an ObservableCollection in the code behind of a user control. It is created when the window loads:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Entities db = new Entities();
ObservableCollection<Image> _imageCollection =
new ObservableCollection<Image>();
IEnumerable<library> libraryQuery =
from c in db.ElectricalLibraries
select c;
foreach (ElectricalLibrary c in libraryQuery)
{
Image finalImage = new Image();
finalImage.Width = 80;
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri(c.url);
logo.EndInit();
finalImage.Source = logo;
_imageCollection.Add(finalImage);
}
}
I need to get the ObservableCollection of images which are created based on the url saved in a database. But I need a ListView or other ItemsControl to bind to it in XAML file like this:
But I can't figure it out how to pass the ObservableCollection to the ItemsSource of that control. I tried to create a class and then create an instance of a class in xaml file but it did not work. Should I create a static resource somehow>
Any help will be greatly appreciated.
Firstly, the ObservableCollection is a local variable. What you need to do is have it as a private global variable and expose it with a public property. You can use the INotifyPropertyChanged interface to have the image data update automagically when the actual collection itself changes.
In your XAML, you then need to set the DataContext to self, and you can then directly bind your public property to the ItemsSource. You may want to use an ItemTemplate for displaying the items in a custom manner.
Cheers,
Adam
Example as requested:
In C#:
public MyWindowClass
{
public ObservableCollection<image> MyImageCollection
{
get;
set;
}
}
In XAML:
<UserControl
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
...
<ListBox ItemsSource="{Binding MyImageCollection}" ItemTemplate="*yourtemplateresource*" />
...
</UserControl>
Now, the reason that I mentioned using INotifyPropertyChanged is that if you try:
MyImageCollection = new ObservableCollection<image>();
The items in the listbox will not automatically update. With an ObservableCollection, however, you do not need to implement INotifyPropertyChanged for basic addition and removal of list items.
You have to set the DataContext of the UserControl to your collection:
DataContext = _imageCollection
You can do that in the UserControl_Loaded() method.
Next you need to bind the ItemsSource of the ListView in the XAML:
<ListView ItemsSource="{Binding}"/>
The {Binding} is equivalent to {Binding .} which binds to the DataContext of the UserControl. If you need "more stuff" in your DataContext you can instead create a class like this:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
...
}
Use this class for the DataContext:
DataContext = new ViewModel();
And replace the binding to bind to the Images property:
<ListView ItemsSource="{Binding Images}"/>
Then you can add another property to ViewModel:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
public String Message { get { ... } set { ... } }
...
}
And bind it to a control:
<TextBlock Text="{Binding Message}"/>
Remember to fire the PropertyChanged event when the Message property is changed in ViewModel. This will update the UI when view-model properties are changed by code.

ListBox does not display text

In my TabControl using WPF,C#.I entering Text to ListBox in one TabItem from the click event in TabControl. But the ListBox does not display the Text. When I debug, I can find that the ListBox has count:1. Here is the code:
namespace Tabcontrol
{
public partial class PresetTab : UserControl //3rd Tabitem ,preset.xaml.cs
{
public PresetTab()
{
InitializeComponent();
}
public void AddPresetmenu(string pMenu)
{
menubox.Items.Add(pMenu); //menubox is listbox
}
}
}
namespace Tabcontrol
{
public partial class ToolBar : UserControl
{
PresetTab tab = new PresetTab();
public ToolBar()
{
InitializeComponent();
}
public void Click(object sender, MouseButtonEventArgs e)
{
Add("TAB MENU");
}
public void Add(string menu)
{
tab.AddPresetmenu(menu); //Im calling from tabcontrol,toolbar.xaml.cs
}
}
}
It would be easier to say for sure if you would have added your XAML code as well, but it seems to me that your adding the strings directly to the Items property and aren't applying a DataTemplate specifying how to display the strings. So either apply a DataTemplate which turns the string into a UIElement, e.g. a TextBlock, or try to add the TextBlocks in your code instead of strings.
public void AddPresetmenu(string pMenu)
{
TextBlock tb= new TextBlock();
tb.Text = pMenu;
menubox.Items.Add(tb);
}
Hope this helps, if not please include your XAML, this will make it easier to spot the problem.

Resources