I am working on WPF MVVM.
I am fetching the records from database and
I have binded ObservableCollection to xaml Itemsource.
Fetch List Code :
private async void getKYCUserDetails()
{
var data = _DataService.Client;
var AllKYCUserList = await data.For<vwKYCUserDetails>().FindEntriesAsync();
lstTempKYCUserDetails= new ObservableCollection<vwKYCUserDetails>(AllKYCUserList);
}
I want to rearrange the ObservableCollection such that System must show clients whose details are complete in nature on top of the report so user can act as per JIT approach.
Xaml :
<syncfusion:SfDataGrid x:Name="dgKYCUser"
ItemsSource="{Binding Path = lstTempKYCUserDetails,Mode=TwoWay}"
SelectedItem="{Binding SelectedItem,Mode=TwoWay}"
NavigationMode="Row"
SelectionMode="Multiple"
ColumnSizer="Auto"
HeaderRowHeight="30"
AllowFiltering="True"
AllowSorting="True"
AllowGrouping="True"
ShowGroupDropArea="True"
HeaderStyle="{StaticResource headerStyle}"
AllowResizingColumns="True"
AllowDraggingColumns="True"
AllowDrop="True"
AutoGenerateColumns="False"
Foreground="#3e4345"
AllowEditing="True"
FrozenColumnCount="2"
BorderBrush="Red"
Background="White"
>
I have multiple columns in sfDatagrid in that I am converting boolean value to image using Converter and show image.
e.g
<syncfusion:GridImageColumn
ImageHeight="20" Width="30" ShowHeaderToolTip="True"
MappingName="bMobileVerify" HeaderText="Mobile Verified"
ValueBinding="{Binding bMobileVerify, Converter={StaticResource ImageConverter}}"
/>
The ImageConverter Code :
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((Boolean)value == true)
{
return new BitmapImage(new Uri(string.Format(#"..\..\Images\{0}", "check.png"), UriKind.Relative));
}
else if ((Boolean)value == false)
{
return new BitmapImage(new Uri(string.Format(#"..\..\Images\{0}", "crosssqr.png"), UriKind.Relative));
}
return new BitmapImage(new Uri(string.Format(#"..\..\Images\{0}", "TransparentBackground.png"), UriKind.Relative));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I want the records which has most ticks should on the top likewise it should have arranged, I don't have any clue how to rearrange it.
you can use ICollectionView
ICollectionView view = CollectionViewSource.GetDefaultView(lstTempKYCUserDetails);
view.SortDescriptions.Add(new SortDescription("tickscounter", ListSortDirection.Descending));
This is a sample source to help explain my explanation
<ItemsControl ItemsSource="{Binding PaggingButtonList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<UserControl Name="{Binding ViewName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I want to dynamically add as in the code above. Unfortunately, the only way to add a View I know is to . So I want to know what to assign to what? Section in to dynamically find and add View file of my project. Thank you
You can use ContentControl to host your UserControl:
<ItemsControl ItemsSource="{Binding ViewList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding Name,Converter={StaticResource NameToContentConverter}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Define ObservableCollection:
public ObservableCollection<object> ViewList { get; set; } =
new ObservableCollection<object>();
and to add Content later
ViewList.Add(new View() { Name = "yourUserControlName" });
Your View Class:
public class View
{
public string Name { get; set; } = "";
}
Since ContentControl.Content expect object and you are passing it as a string
you can use Converter.
Converter:
public class NameToContentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value != null)
{
Type userControl = Type.GetType(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name +"."+ value,null,null);
return Activator.CreateInstance(userControl);
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
to know more about Activator see here
I've written a converter BoolToStringConverter. The converter has two properties TrueString and FalseString. Here's how I've used it in XAML
<UserControl.Resources>
<local:BooleanToStringConverter x:Key="BooleanToStringConverter" TrueString="{Binding Strings.Open, Source={StaticResource MyStrings}}"></local:BooleanToStringConverter>
</UserControl.Resources>
This compiles ok, but I get an xml parse exception when running it. If I change the setting of the TrueString property to TrueString = "Open" it all works fine.
Here's the converter being used:
<Button x:Name="MyButton" Content="{Binding Path=IsOpen, Converter={StaticResource BooleanToStringConverter}}" Command="{Binding MyCommand}" VerticalAlignment="Top" Style="{StaticResource MyStyle}" Margin="0,2,10,2"/>
Any ideas what is wrong? All I want to do as set a property of a local resource to a localized value.
EDIT Here's my converter class
public class BooleanToStringConverter : IValueConverter
{
public BooleanToStringConverter()
{
}
public string TrueString
{
get;
set;
}
public string FalseString
{
get;
set;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool boolValue = System.Convert.ToBoolean(value, CultureInfo.InvariantCulture);
return boolValue ? TrueString : FalseString;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here's the runtime exception message:
A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in System.Windows.dll
Additional information: Set property 'Optimize.Client.Presentation.BooleanToStringConverter.FalseString' threw an exception. [Line: 18 Position: 86]
You cannot bind to the TrueString and FalseString properties. From the MSDN help:
in order to be the target of a binding, the property must be a dependency propert
You can try using the ConverterParameter part of the binding for your xaml
<Button x:Name="MyButton" Content="{Binding Path=IsOpen, Converter={StaticResource BooleanToStringConverter}, ConverterParameter=Open}"
Command="{Binding MyCommand}" VerticalAlignment="Top"
Style="{StaticResource MyStyle}" Margin="0,2,10,2"/>
You could also make your converter less generic and only handle Open/Closed strings.
Another option is to have your value converter extend DependencyObject, and convert your properties to DependencyProperties.
You can also set the public properties in your XAML like this:
<localHelpers:BoolToTextConverter x:Key="boolToTextConverter">
<localHelpers:BoolToTextConverter.TrueText>
Sent
</localHelpers:BoolToTextConverter.TrueText>
<localHelpers:BoolToTextConverter.FalseText>
Not Sent
</localHelpers:BoolToTextConverter.FalseText>
</localHelpers:BoolToTextConverter>
The full example is on my blog post here.
I'm trying to make a custom converter that inherits from DependencyObject, but it doesn't work:
Converter:
public class BindingConverter : DependencyObject , IValueConverter
{
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(BindingConverter), new PropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
Debug.Assert(Value != null); //fails
return Value;
}
public object ConvertBack(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Xaml:
<StackPanel x:Name="this">
<!--works-->
<ContentControl Content="{Binding ActualHeight, ElementName=this}"/>
<!--doesn't work-->
<ContentControl>
<Binding>
<Binding.Converter>
<BindingConverter Value="{Binding ActualHeight, ElementName=this}" />
</Binding.Converter>
</Binding>
</ContentControl>
<TextBlock Text="{Binding Animals}"/>
</StackPanel>
Am I missing out anything?
I have some places in my projects where I needed similar functionality. Can't show you exact sample, just an idea:
perhaps you have to inherit from FrameworkElement, not IValueConverter, Something like this:
public class BindingHelper : FrameworkElement
in the BindingHelper class, set Visibility to Collapsed and IsHitTestVisible to false;
to make it working, insert it into visual tree directly. In your example, it should be a child of the StackPanel. So, it will have the same DataContext as other StackPanel children;
then, you can add one ore more dependency properties depending on your needs. For example, you might have single property for the source of data and some different properties which you then will use as converter return values. Handle all changes to the source property in your BindingHelper class and change output properties accordingly;
bind other controls to properties of the BindingHelper class using ElementName syntax
in Silverlight, ActualHeight and ActualWidth properties don't do notifications on property updates. So, binding to them won't work.
Note! ActualHeight property's binding is buggy on binding!
Why you inherit DependencyObject when coding a converter? You should just implement IValueConverter.
Try that,
First add MyConverter by the key of "MyConverterResource" on your resources then,
You can do than on XAML side or on cs side by
//You may do it on XAML side <UserControl.Resources>...
this.Resources.Add("MyConverterResource",new MyConverter());
<TextBlock Text="{Binding ActualHeight,ElementName=this
,Converter=MyConverterResource}"/>
public class MyConverter: IValueConverter
{
public object Convert(object value, Type targetType
, object parameter,Globalization.CultureInfo culture)
{
return "Your Height is:"+Value.toString();
}
}
Hope helps
The obvious solution would be to have a row number property on a ModelView element, but the drawback is that you have to re-generate those when you add records or change sort order.
Is there an elegant solution?
I think you have the elegant solution, but this works.
XAML:
<ListView Name="listviewNames">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn
Header="Number"
DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}},
Converter={StaticResource IndexConverter}}" />
<GridViewColumn
Header="Name"
DisplayMemberBinding="{Binding Path=Name}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
ValueConverter:
public class IndexConverter : IValueConverter
{
public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
{
ListViewItem item = (ListViewItem) value;
ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
int index = listView.ItemContainerGenerator.IndexFromContainer(item);
return index.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
If you have a dynamic list where items are added, deleted or moved, you can still use this very nice solution and simply let the currentview of your listview refresh itself after the changements in your source list are done.
This code sample removes the current item directly in the data source list "mySourceList" (which is in my case an ObservableCollection) and finally updates the line numbers to correct values .
ICollectionView cv = CollectionViewSource.GetDefaultView(listviewNames.ItemsSource);
if (listviewNames.Items.CurrentItem != null)
{
mySourceList.RemoveAt(cv.CurrentPosition);
cv.Refresh();
}
First you need to set the AlternationCount to items count+1, for instance:
<ListView AlternationCount="1000" .... />
Then AlternationIndex will show the real index, even during the scrolling:
<GridViewColumn
Header="#" Width="30"
DisplayMemberBinding="{Binding (ItemsControl.AlternationIndex),
RelativeSource={RelativeSource AncestorType=ListViewItem}}" />
This will work like a charm,
I don't know about performance,
Still we can give it a try
Create a Multi Value Converter
public class NumberingConvertor : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values != null && values.Any() && values[0] != null && values[1] != null)
{
//return (char)(((List<object>)values[1]).IndexOf(values[0]) + 97);
return ((List<object>)values[1]).IndexOf(values[0]) + 1;
}
return "0";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
}
and
your Xaml like this
<ItemsControl ItemsSource="{Binding ListObjType}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label>
<MultiBinding Converter="{StaticResource NumberingConvertor}">
<Binding Path="" />
<Binding Path="ItemsSource"
RelativeSource="{RelativeSource AncestorType=ItemsControl}" />
</MultiBinding>
</Label>
<TextBlock Text="{Binding }" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Idea is to send Object and list both to the converter and let converter decide the number. You can modify converter to display ordered list.
Here is another way, including code comments that will help you understand how it works.
public class Person
{
private string name;
private int age;
//Public Properties ....
}
public partial class MainWindow : Window
{
List<Person> personList;
public MainWindow()
{
InitializeComponent();
personList= new List<Person>();
personList.Add(new Person() { Name= "Adam", Agen= 25});
personList.Add(new Person() { Name= "Peter", Agen= 20});
lstvwPerson.ItemsSource = personList;
//After updates to the list use lstvwPerson.Items.Refresh();
}
}
The XML
<GridViewColumn Header="Number" Width="50"
DisplayMemberBinding="{
Binding RelativeSource= {RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}},
DELETE Path=Content, DELETE
Converter={StaticResource IndexConverter},
ConverterParameter=1
}"/>
RelativeSource is used in particular binding cases when we try to bind a property of a given object to another property of the object itself [1].
Using Mode=FindAncestor we can traverse the hierarchy layers and get a specified element, for example the ListViewItem (we could even grab the GridViewColumn). If you have two ListViewItem elements you can specify which you want with "AncestorLevel = x".
Path: Here I simply take the content of the ListViewItem (which is my object "Person").
Converter Since I want to display row numbers in my Number column and not the object Person I need to create a Converter class which can somehow transform my Person object to a corresponding number row. But its not possible, I just wanted to show that the Path goes to the converter. Deleting the Path will send the ListViewItem to the Converter.
ConverterParameter Specify a parameter you want to pass to the IValueConverter class. Here you can send the state if you want the row number to start at 0,1,100 or whatever.
public class IndexConverter : IValueConverter
{
public object Convert(object value, Type TargetType, object parameter, System.Globalization.CultureInfo culture)
{
//Get the ListViewItem from Value remember we deleted Path, so the value is an object of ListViewItem and not Person
ListViewItem lvi = (ListViewItem)value;
//Get lvi's container (listview)
var listView = ItemsControl.ItemsControlFromItemContainer(lvi) as ListView;
//Find out the position for the Person obj in the ListView
//we can get the Person object from lvi.Content
// Of course you can do as in the accepted answer instead!
// I just think this is easier to understand for a beginner.
int index = listView.Items.IndexOf(lvi.Content);
//Convert your XML parameter value of 1 to an int.
int startingIndex = System.Convert.ToInt32(parameter);
return index + startingIndex;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I found solution that will work even in case when you need to move your elements inside the collection. So actually what we need to do for it is notify dummy property "ListNumbersNotify" every time our collection is changed and bind everything with that tricky MultiBinding converter.
XAML:
<Window ...
x:Name="This">
...
<ListView Name="ListViewCurrentModules">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label>
<MultiBinding Converter="{helpers:NumberingConvertor}">
<Binding Path="" />
<Binding ElementName="ListViewCurrentModules" />
<Binding Path="ListNumbersNotify" ElementName="This" />
</MultiBinding>
</Label>
<Border>
...
</Border>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Converter:
public abstract class MultiConvertorBase<T> : MarkupExtension, IMultiValueConverter
where T : class, new()
{
public abstract object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
public virtual object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
_converter = new T();
return _converter;
}
private static T _converter = null;
}
public class NumberingConvertor : MultiConvertorBase<NumberingConvertor>
{
public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return ((ListView)values[1]).Items.IndexOf(values[0]) + 1;
}
}
Code behind:
public partial class AddModulesWindow: Window, INotifyPropertyChanged
{
...
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string prop)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
public object ListNumbersNotify { get; }
public AddModulesWindow(ICore core)
{
InitializeComponent();
this.core = core;
CurrentModuleInfos = new ObservableCollection<ModuleInfo>(core.Modules.Select(m => m?.ModuleInfo));
CurrentModuleInfos.CollectionChanged += CurrentModuleTypes_CollectionChanged;
ListViewCurrentModules.ItemsSource = CurrentModuleInfos;
}
private void CurrentModuleTypes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("ListNumbersNotify");
}
It's the addition to answer of amaca for problems found by Allon Guralnek and VahidN. Scrolling problem is solved with setting ListView.ItemsPanel to StackPanel in XAML:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
This replacement of default VirtualizingStackPanel with simple StackPanel disables automatic regeneration of internal collection of ListViewItem. So indices would not chaotically change when scrolling. But this replacement can decrease perfomance on large collections. Also, dynamic numeration changes can be achieved with call CollectionViewSource.GetDefaultView(ListView.ItemsSource).Refresh() when ItemsSource collection changed. Just like with ListView filtering. When I tried to add handler with this call on event INotifyCollectionChanged.CollectionChanged my ListView output was duplicating last added row (but with correct numeration). Fixed this by placing refresh call after every collection change in code. Bad solution, but it works perfect for me.
amaca answer is great for static lists. For dynamic:
We should use MultiBinding, second binding is for changing collection;
After deleting ItemsControl not contains deleted object, but ItemContainerGenerator contains.
Converter for dynamic lists (I use it for TabControl TabItem's):
public class TabIndexMultiConverter : MultiConverterBase
{
public override object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
TabItem tabItem = value.First() as TabItem;
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(tabItem);
object context = tabItem?.DataContext;
int idx = ic == null || context == null // if all objects deleted
? -1
: ic.Items.IndexOf(context) + 1;
return idx.ToString(); // ToString necessary
}
}
By following best answer solution I found an issue when indexes still not updated after removing/replacing items inside list view. To solve that there is one not so clear hint (I propose to use it in small collections): after executing item removing/replacing you should invoke ObservableCollection(INotifyCollectionChanged).CollectionChanged event with Reset action. This is possible to make with extending existing ObservableCollection, which is ItemsSource or use reflection when this is not possible.
Ex.
public class ResetableObservableCollection<T> : ObservableCollection<T>
{
public void NotifyReset()
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
private void ItemsRearranged()
{
Items.NotifyReset();
}
Here's my little converter which works great as of WPF in 2017 with .NET 4.7.2, including with the VirtualizingStackPanel fully enabled:
[ValueConversion(typeof(IList), typeof(int))]
public sealed class ItemIndexConverter : FrameworkContentElement, IValueConverter
{
public Object Convert(Object data_item, Type t, Object p, CultureInfo _) =>
((IList)DataContext).IndexOf(data_item);
public Object ConvertBack(Object o, Type t, Object p, CultureInfo _) =>
throw new NotImplementedException();
};
Add an instance of this IValueConverter to the Resources of the GridViewColumn.CellTemplate, or elsewhere. Or, instantiate it in-situ on the Binding of the bound element, like I show here. In any case, you need to create an instance of the ItemIndexConverter and don't forget to bind the whole source collection to it. Here I'm pulling a reference to the source collection out of the ItemsSource property of the ListView--but this entails some unrelated hassles over accessing the XAML root, so if you have a better and easier way to refer to the source collection, you should do so.
As for accessing a property on the XAML root, the ListView root in XAML is given the name w_root, and the XAML markup extension {x:Reference ...} is used to access the XAML root element. I don't think "ElementName" binding will work here since the reference occurs in a template context.
<ListView x:Class="myApp.myListView"
x:Name="w_root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:myApp"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListView.View>
<GridView>
<GridViewColumn Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<Binding>
<Binding.Converter>
<local:ItemIndexConverter DataContext="{Binding
Source={x:Reference w_root},
Path=(ItemsControl.ItemsSource)}" />
</Binding.Converter>
</Binding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
That's it! It seems to work pretty quickly with a large number of rows, and again, you can see that the reported indices are correct when arbitrarily scrolling around, and that VirtualizingStackPanel.IsVirtualizing is indeed set to True.