I have a WPF-MVVM application...
I have 3 Radio button controls - with three options => "Individual", "Group" and "Both".
All 3 have same Group name...that means only one of these radio button can be selected.
I can have three properties in viewmodel...for each of these three options...and can check which one is selected.
Function()
{
if (Is_Individual_property)
{
// Individual selected
}
if (Is_Group_property)
{
// group selected
}
if (Is_Both_property)
{
// Both selected
}
}
But I think this is not best approach.
Can I have just one property in viewmodel and bind the values accordingly ?
How about having a single property and managing multiple values using a converter. For example:
XAML:
<Grid>
<Grid.Resources>
<local:BooleanToStringValueConverter x:Key="BooleanToStringValueConverter" />
</Grid.Resources>
<StackPanel>
<TextBlock Text="{Binding Property1}" />
<RadioButton Name="RadioButton1"
GroupName="Group1"
Content="Value1"
IsChecked="{Binding Path=Property1, Converter={StaticResource BooleanToStringValueConverter}, ConverterParameter=Value1}" />
<RadioButton Name="RadioButton2"
GroupName="Group1"
Content="Value2"
IsChecked="{Binding Path=Property1, Converter={StaticResource BooleanToStringValueConverter}, ConverterParameter=Value2}" />
<RadioButton Name="RadioButton3"
GroupName="Group1"
Content="Value3"
IsChecked="{Binding Path=Property1, Converter={StaticResource BooleanToStringValueConverter}, ConverterParameter=Value3}" />
</StackPanel>
</Grid>
Converter:
public class BooleanToStringValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (System.Convert.ToString(value).Equals(System.Convert.ToString(parameter)))
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (System.Convert.ToBoolean(value))
{
return parameter;
}
return null;
}
}
Class:
public class MyClass : INotifyPropertyChanged
{
private String _property1;
public String Property1
{
get { return _property1; }
set { _property1 = value; RaisePropertyChanged("Property1"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(String propertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyClass() { Property1 = "Value1" };
}
}
Related
I want to use linq grouping as itemssource for checkbox listview
It my data model:
public class GroupItem
{
public string name GroupName { get; set; }
public string boolean GroupItemFlag { get; set; }
}
It view model (data context):
...
IEnumerable<GroupItem> _groupItems;
public IEnumerable<IGrouping<string,GroupItem>> Groups
{
get { return _groupItems.GroupBy(__item=>__item.GroupName); }
}
...
it view:
...
<ListView ItemsSource={Binding Groups}>
<ListView.ItemTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Converter={StaticResource groupingToBooleanConverter}, Mode=TwoWay}"/>
<TextBlock Text="{Binding Key}"/>
</StackPanel>
</ListView.ItemTemplate>
</ListView>
...
groupingToBooleanConverter code:
public class GroupingToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var group = value as IGrouping<string,GroupItem>;
return group.Any(__item => __item.GroupItemFlag);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// problem here
// how to set for all group items GroupItemFlag=(bool)value;
}
}
I don't want create one more class "Group" for this, so i use IGrouping and converter.
May be I chose the wrong way?
You can achieve it even without converter:
<CheckBox IsChecked="{Binding Path=GroupItemFlag, Mode=OneWay}"
Command="{Binding DataContext.ToogleGroupCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}"/>
void ToogleGroupCommand_Execute(IGrouping<string, GroupItem> group){
bool newValue = !group.First().GroupItemFlag;
foreach(var item in group) item.GroupItemFlag = newValue;
}
if you are not using MVVM, then just handle Checked and Unchecked events.
However, if you are using MVVM, I highly recommend you to create Group class as ViVi suggests:
public MainViewModel()
{
Groups = _groupItems.GroupBy(i => i.GroupName).Select(i => new GroupViewModel(i.Key, i);
}
public Groups[] Groups {get;}
public class GroupViewModel
{
public GroupViewModel(string name, IEnumerable<GroupItem> items)
{
Items = items;
}
public string Name { get; }
public IEnumerable<GroupItem> Items { get; }
public bool? IsChecked
{
get
{
if (Items.All(i => i.GroupItemFlag)) return true;
if (Items.Any(i => i.GroupItemFlag)) return null;
return false;
}
set
{
foreach (var item in Items)
{
item.GroupItemFlag = value.GetValueOrDefault();
}
}
}
}
In my View Model (VM) I have an ObservableCollection of items. In my view I am binding to the collection. I have created a few user controls that have a dependency property that i am binding to called STCode. So for example a "Tag" object will have a "Name" property of type String and a "value" property of type integer.
In my ViewModel
Constructor
Tags.Add(new Tag("Tag1",111));
Tags.Add(new Tag("Tag2",222));
Tags.Add(new Tag("Tag3",333));
Tags.Add(new Tag("Tag4",444));
public ObservableCollection<Tag> Tags
{
get
{
return _TagList;
}
set
{
if (value != _TagList)
{
_TagList = value;
}
}
}
In my View
<my:UserControl1 x:Name="control1" Margin="12,89,0,0" HorizontalAlignment="Left" Width="257" Height="249" VerticalAlignment="Top" STCode="{Binding Path=Value}"/>
This will bind to the First items value property in the ObservableCollection (Showing "Tag1" value). Is there anyway that I can get a specific "tag" object from the observableCollection by specifying the string Name property? So basically if I had 3 instances of my usercontrol in the view, on each control I would like to specify the "Name" property of the Tag object as a string in XAML, and in return bind that specific control to that specific tags integer "Value" property?
I hope this makes sense
Model
public class Tag : ModelBase
{
private int _value;
public string Tagname { get; set; }
public int Value
{
get
{
return _value;
}
set
{
_value = value;
NotifyPropertyChanged("Value");
}
}
}
ModelBase
public class ModelBase :INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Since your UserControls are bound to the Collection itself and not the Item on the collection (the converter does this job internally) you must call PropertyChanged on the whole Collection when you want to refresh the bindings on your usercontrols.
Edit: Full solution
ViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
public ObservableCollection<Tag> Tags { get; private set; }
public MainWindowViewModel()
{
Tags = new ObservableCollection<Tag>();
Tags.Add(new Tag("Tag1", 111));
Tags.Add(new Tag("Tag2", 222));
Tags.Add(new Tag("Tag3", 333));
Tags.Add(new Tag("Tag4", 444));
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public void ChangeRandomTag()
{
var rand = new Random();
var tag = Tags[rand.Next(0, Tags.Count - 1)];
tag.Value = rand.Next(0, 1000);
OnPropertyChanged("Tags");
}
}
View XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<wpfApplication1:MyConverter x:Key="MyConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Tags}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="1"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="1" Orientation="Vertical">
<Button x:Name="buttonChangeRandomTag"
Click="ButtonChangeRandomTag_OnClick"
Content="Change Random Tag Value" />
<TextBlock Text="{Binding Tags, Converter={StaticResource MyConverter}, ConverterParameter=Tag1}" />
<TextBlock Text="{Binding Tags, Converter={StaticResource MyConverter}, ConverterParameter=Tag2}" />
<TextBlock Text="{Binding Tags, Converter={StaticResource MyConverter}, ConverterParameter=Tag3}" />
<TextBlock Text="{Binding Tags, Converter={StaticResource MyConverter}, ConverterParameter=Tag4}" />
</StackPanel>
</Grid>
View Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new MainWindowViewModel();
InitializeComponent();
}
private void ButtonChangeRandomTag_OnClick(object sender, RoutedEventArgs e)
{
(DataContext as MainWindowViewModel).ChangeRandomTag();
}
}
Converter:
[ValueConversion(typeof(ObservableCollection<Tag>), typeof(int))]
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var collection = value as ObservableCollection<Tag>;
var key = parameter as string;
if (collection == null || parameter == null)
return 0;
var result = collection.FirstOrDefault(item => item.Name.Equals(key));
if (result == null)
return 0;
return result.Value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Tag Class:
public class Tag : INotifyPropertyChanged
{
private string name;
private int value;
public string Name
{
get { return name; }
set
{
if (value == name) return;
name = value;
OnPropertyChanged("Name");
}
}
public int Value
{
get { return value; }
set
{
if (value == this.value) return;
this.value = value;
OnPropertyChanged("Value");
}
}
public Tag(string name, int value)
{
Value = value;
Name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
You could use a ValueConverter to do that for you:
[ValueConversion(typeof(string), typeof(string))]
public class StringToTagPropertyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value != typeof(ObservableCollection<Tag>)) return
DependencyProperty.UnsetValue;
if (parameter as string == null) return DependencyProperty.UnsetValue;
ObservableCollection<Tag> tagObject = (ObservableCollection<Tag>)value;
string returnValue = tagObject.Where(t => t.Name.ToLower() ==
parameter.ToString().ToLower()).FirstOrDefault();
return returnValue ?? DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
You would use it like this:
<my:UserControl1 x:Name="control1" Margin="12,89,0,0" HorizontalAlignment="Left"
Width="257" Height="249" VerticalAlignment="Top" STCode="{Binding Tags,
Converter={StaticResource StringToTagPropertyConverter},
ConverterParameter="Name"}" />
By changing the value of the ConverterParameter, you can get the ValueConverter to return different properties of your 'tag object'. I am assuming that you know how to add a value converter in XAML.
In this silverlight grid cell, the display value does not update when the edit value is updated, any ideas why?
<sdk:DataGridTemplateColumn Header="Bid Qty" IsReadOnly="True">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{Binding Path=BidPrice.Quantity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource CustomDoubleToStringConverter}}"
Foreground="{Binding Path=BidPrice.TextColour}"
ToolTipService.ToolTip="{Binding Path=BidPrice.ToolTip}"
ToolTipService.Placement="Right" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=BidPrice.Quantity, Mode=TwoWay, Converter={StaticResource CustomDoubleToStringConverter}}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
Code (very simplified) :
// field is bound to BidPrice on this
public class PriceStackLine : INotifyPropertyChanged
{
public ProductPrice BidPrice { get; set; }
}
// BidPrice is a ProductPrice with a Quantity property
public class ProductPrice : INotifyPropertyChanged
{
private double _qty;
public double Quantity
{
get
{
return _qty;
}
set
{
_qty = value;
NotifyPropertyChanged("Quantity");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// converter
public class CustomDoubleToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double num = (double)value;
if (num == 0)
return string.Empty;
return num;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
double result = 0;
double.TryParse(value.ToString(), out result);
return result;
}
}
UPDATE - FIXED! The TextColour was null on the new edits which was probably defaulting to transparent, that is why I couldnt see the edit
I am trying to make a button Enabled only if a listbox has at least one item in it. I am adding listboxitems by selecting them from AutoCompleteBox list. I tried to bind isEnabled property to the listbox and use BoolConverter to validate if listbox has content.
<Button x:Name="DisabledButton" Click="RemoveButton_Click" Content="Disabled Button" IsEnabled="{Binding ItemsSource, ElementName=EntitiesListBox,Converter={StaticResource CountToBooleanConverter}}" />
I am missing something. I am wondering if someone can tell me what is wrong. Any ideas are highly appreciated!
Silverlight XAML:
<UserControl.Resources>
<local:BoolToOppositeBoolConverter x:Key="CountToBooleanConverter" />
<local:CountGreaterThanZeroConverter x:Key="CountGreaterThanZeroConverter" />
</UserControl.Resources>
<StackPanel x:Name="LayoutRoot" Background="White" Width="150">
<TextBlock Text="{Binding ElementName=MyAutoCompleteBox, Path=SelectedItem, TargetNullValue='No item selected', StringFormat='Selected Item: {0}'}" />
<sdk:AutoCompleteBox x:Name="MyAutoCompleteBox" IsTextCompletionEnabled="True" ItemsSource="{Binding Items}" />
<Button x:Name="AddButton" Click="AddButton_Click" Content="AddButton" />
<Button x:Name="RemoveButton" Click="RemoveButton_Click" Content="RemoveButton" />
<Button x:Name="DisabledButton" Click="RemoveButton_Click" Content="Disabled Button" IsEnabled="{Binding ItemsSource, ElementName=ListBox,Converter={StaticResource CountToBooleanConverter}}" />
<Button x:Name="DisabledButton2" Click="RemoveButton_Click" Content="Disabled Button" IsEnabled="{Binding ItemsSource.Count, ElementName=ListBox, Converter={StaticResource CountGreaterThanZeroConverter}}" />
</StackPanel>
Code behind:
public partial class MainPage : UserControl
{
string currentItemText;
public ObservableCollection<string> Items
{
get;
private set;
}
public MainPage()
{
InitializeComponent();
Items = new ObservableCollection<string>();
Items.Add("One");
Items.Add("Two");
Items.Add("Three");
Items.Add("Four");
DataContext = this;
}
private void AddButton_Click(object sender, RoutedEventArgs e)
{
currentItemText = MyAutoCompleteBox.SelectedItem.ToString();
ListBox.Items.Add(currentItemText);
ApplyDataBinding();
}
private void RemoveButton_Click(object sender, RoutedEventArgs e)
{
if (ListBox.SelectedItems != null)
{
int count = ListBox.SelectedItems.Count - 1;
for (int i = count; i >= 0; i--)
{
ListBox.Items.Remove(ListBox.SelectedItems[i]);
}
ApplyDataBinding();
}
}
private void ApplyDataBinding()
{
MyAutoCompleteBox.ItemsSource = null;
}
}
public class CountGreaterThanZeroConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (int)value > 0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
Why not just bind to the count of the itemssource and use an observable collection?
<Button x:Name="DisabledButton" Click="RemoveButton_Click" Content="Disabled Button" IsEnabled="{Binding ItemsSource.Count, ElementName=ListBox,Converter={StaticResource CountGreaterThanZeroConverter}}" />
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return (int)value > 0;
}
And in your viewmodel
public ObservableCollection<string> Items
{
get;
private set;
}
does anyone know if there is a simple way to bind a textblock to a List.
What I've done so far is create a listview and bind it to the List and then I have a template within the listview that uses a single textblock.
what I'd really like to do is just bind the List to a textblock and have it display all the lines.
In Winforms there was a "Lines" property that I could just throw the List into, but I'm not seeing it on the WPF textblock, or TextBox.
Any ideas?
did I miss something simple?
Here's the code
<UserControl x:Class="QSTClient.Infrastructure.Library.Views.WorkItemLogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500" Height="400">
<StackPanel>
<ListView ItemsSource="{Binding Path=Logs}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Log Message">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
and the WorkItem Class
public class WorkItem
{
public string Name { get; set; }
public string Description { get; set; }
public string CurrentLog { get; private set; }
public string CurrentStatus { get; private set; }
public WorkItemStatus Status { get; set; }
public ThreadSafeObservableCollection<string> Logs{get;private set;}
I'm using Prism to create the control and put it into a WindowRegion
WorkItemLogView newView = container.Resolve<WorkItemLogView>();
newView.DataContext = workItem;
regionManager.Regions["ShellWindowRegion"].Add(newView);
thanks
Convert your List to a single string with "\r\n" as the delimiter in between. and bind that to the TextBlock. Make sure that the TextBlock is not restricted with its height , so that it can grow based on the number of lines.
I would implement this as a Value Converter to XAML Binding which converts a List of strings to a single string with new line added in between
<TextBlock Text="{Binding Path=Logs,Converter={StaticResource ListToStringConverter}}"/>
The ListToStringConverter would look like this:
[ValueConversion(typeof(List<string>), typeof(string))]
public class ListToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType != typeof(string))
throw new InvalidOperationException("The target must be a String");
return String.Join(", ", ((List<string>)value).ToArray());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
if you use the converter it works for the first time perfect,
but if one or more logging comes to the logging list, there is no update on your binding, because the converter works only at the first time.
all controls that are no item controls doesn't subscribe to the listchanged event!
here is a little code for this scenario
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace BindListToTextBlock
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private WorkItem workItem;
public MainWindow() {
this.WorkItems = new ObservableCollection<WorkItem>();
this.DataContext = this;
this.InitializeComponent();
}
public class WorkItem
{
public WorkItem() {
this.Logs = new ObservableCollection<string>();
}
public string Name { get; set; }
public ObservableCollection<string> Logs { get; private set; }
}
public ObservableCollection<WorkItem> WorkItems { get; set; }
private void Button_Click(object sender, RoutedEventArgs e) {
this.workItem = new WorkItem() {Name = string.Format("new item at {0}", DateTime.Now)};
this.workItem.Logs.Add("first log");
this.WorkItems.Add(this.workItem);
}
private void Button_Click_1(object sender, RoutedEventArgs e) {
if (this.workItem != null) {
this.workItem.Logs.Add(string.Format("more log {0}", DateTime.Now));
}
}
}
}
the xaml
<Window x:Class="BindListToTextBlock.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:BindListToTextBlock="clr-namespace:BindListToTextBlock"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.Resources>
<BindListToTextBlock:ListToStringConverter x:Key="ListToStringConverter" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0"
Content="Add item..."
Click="Button_Click" />
<Button Grid.Row="1"
Content="Add some log to last item"
Click="Button_Click_1" />
<ListView Grid.Row="2"
ItemsSource="{Binding Path=WorkItems}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Log Message">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Logs, Converter={StaticResource ListToStringConverter}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
the converter
using System;
using System.Collections;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace BindListToTextBlock
{
public class ListToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is IEnumerable) {
return string.Join(Environment.NewLine, ((IEnumerable)value).OfType<string>().ToArray());
}
return "no messages yet";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return DependencyProperty.UnsetValue;
}
}
}
EDIT
here is a quick solution for the update propblem (this can be also made with a attached property)
public class CustomTextBlock : TextBlock, INotifyPropertyChanged
{
public static readonly DependencyProperty ListToBindProperty =
DependencyProperty.Register("ListToBind", typeof(IBindingList), typeof(CustomTextBlock), new PropertyMetadata(null, ListToBindPropertyChangedCallback));
private static void ListToBindPropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var customTextBlock = o as CustomTextBlock;
if (customTextBlock != null && e.NewValue != e.OldValue) {
var oldList = e.OldValue as IBindingList;
if (oldList != null) {
oldList.ListChanged -= customTextBlock.BindingListChanged;
}
var newList = e.NewValue as IBindingList;
if (newList != null) {
newList.ListChanged += customTextBlock.BindingListChanged;
}
}
}
private void BindingListChanged(object sender, ListChangedEventArgs e)
{
this.RaisePropertyChanged("ListToBind");
}
public IBindingList ListToBind
{
get { return (IBindingList)this.GetValue(ListToBindProperty); }
set { this.SetValue(ListToBindProperty, value); }
}
private void RaisePropertyChanged(string propName)
{
var eh = this.PropertyChanged;
if (eh != null) {
eh(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
here is the usage for the CustomTextBlock (not tested)
<TextBlock Text="{Binding Path=ListToBind, RelativeSource=Self, Converter={StaticResource ListToStringConverter}}"
ListToBind={Binding Path=Logs} />
#Fueled hope this helps
I'll shamelessly post a link to my answer of a very similar question: Binding ObservableCollection<> to a TextBox.
Like punker76 said, if you bind your Text to a collection it will update when you set the collection, but not when the collection changes. This link demonstrates an alternative to punker76's solution (the trick is to multi-bind to the collection's count too).
For concat collection of objects :
/// <summary>Convertisseur pour concaténer des objets.</summary>
[ValueConversion(typeof(IEnumerable<object>), typeof(object))]
public class ConvListToString : IValueConverter {
/// <summary>Convertisseur pour le Get.</summary>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return String.Join(", ", ((IEnumerable<object>)value).ToArray());
}
/// <summary>Convertisseur inverse, pour le Set (Binding).</summary>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
Juste think to overide the ToString() of your object.