WPF GridView different stringformat for each item on gridviewcolumn binding - wpf

I'm trying to bind the StringFormat of a column binding depending on each datacontext item's individually.
Here's the sample code:
xaml:
<ListView ItemsSource="{Binding Symbols}">
<ListView.View>
<GridView x:Name="gw">
<GridView.Columns>
<GridViewColumn Header="Symbol" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price, StringFormat={Binding StringFormat}}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
code behind:
public ObservableCollection<symbol> Symbols { get;set;}
public class symbol : INotifyPropertyChanged
{
#region INotify Handler
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
private double _price;
public double Price
{
get => _price;
set
{
_price = value;
OnPropertyChanged(nameof(Price));
}
}
private int _decimalplaces;
public int decimalplaces
{
get => _decimalplaces;
set
{
_decimalplaces = value;
OnPropertyChanged(nameof(decimalplaces));
if (value == 0)
StringFormat = "0";//no decimal
else
StringFormat = $"0.{new string('0', value)}";//like 0.000 for 3 decimal places
}
}
public string StringFormat { get; set; }
}
The StringFormat={Binding StringFormat} is not possible, I've just put it there to demonstrate what I exactly wanted. Each item's (symbol) format is different.
It doesn't matter if I need to add the columns in code behind, I can do it but I just don't know how to.
Any suggestions? Thank you.

Update:
There is a solution I didn't want to use but now I believe it's the only logical way.
private double _price;
public double Price
{
get => _price;
set
{
_price = value;
OnPropertyChanged(nameof(Price));
StringPrice = value.ToString(format);
}
}
private string _stringprice;
public string StringPrice
{
get => _stringprice;
set {
_stringprice = value;
OnPropertyChanged(nameof(StringPrice));
}
}
and using it in XAML:
<GridViewColumn Header="Price" DisplayMemberBinding="{Binding StringPrice}" />

Related

Binding data of celltemple

<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Tên Mặt Hàng" CellTemplate="{StaticResource Ten}">
<GridViewColumn.CellTemple>
<DataTemplate x:Key="Ten">
<TextBlock Text="{Binding Ten}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemple>
</GridViewColumn> </GridView>
every one, I have code, I want to binding Text of textBlock to use data again in other files, so what can I do?
You can create a ViewModel of this view. And bind the viewModel property to this view. For example:
public class NamViewModel: INotifyPropertyChanged {
private string _ten;
public string Ten {
get
{
return _ten;
}
set
{
_ten = value;
this.RaisePropertyChanged("Ten");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then you can bind this viewModel in the constructor of the view. For example:
public partial class NamView : Window {
public NamView() {
InitializeComponent();
this.DataContext = new NamViewModel();
}
}

WPF Get the associated ListView in a GridView

I'm trying to implement a custom GridView for the ListView, but I need to find a way to get the ListView that is associated with the GridView, since I need to access some of the ListView properties (Width, Items, Template...).
I found an old post that was asking the same question Get the parent listview from a gridview object but it never got an answer...
If anyone has an idea, I would be glad :)
EDIT: Here some basic code from the custom GridView
public class GridViewEx : GridView
{
public ListView Owner {get; set;} // This is what I need to get
public GridViewEx()
{
}
}
EDIT2: I found another solution than the one presented by mm8. Since I also needed a custom GridViewHeaderRowPresenter, which is used in the ListView Scrollviewer Style, here is what I came up with (as for now):
public class GridViewHeaderRowPresenterEx : GridViewHeaderRowPresenter
{
private GridViewEx _GridView;
public GridViewHeaderRowPresenterEx()
{
Loaded += OnLoaded;
Unloaded += OnUnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (this.GetVisualParent<ListView>() is ListView lv && lv.View is GridViewEx gridView)
{
_GridView = gridView;
_GridView.Owner = lv;
}
}
private void OnUnLoaded(object sender, RoutedEventArgs e)
{
if (_GridView != null)
_GridView.Owner = null;
}
}
And here is the extension method to get the ListView from the custom GridViewHeaderRowPresenter:
public static class DependencyObjectExtensions
{
public static T GetVisualParent<T>(this DependencyObject depObj) where T : DependencyObject
{
if (VisualTreeHelper.GetParent(depObj) is DependencyObject parent)
{
var result = (parent as T) ?? GetVisualParent<T>(parent);
if (result != null)
return result;
}
return null;
}
}
The GridViewHeaderRowPresenter Loaded event is called when a GridView is added to a ListView, and the Unloaded event is called when the GridView is removed from the ListView.
I prefer this solution over the one from mm8, since it required (if I'm not mistaken) the ListView to have Items in order to work.
Thanks for the suggestions :)
You could override the PrepareItem method and use the ItemsControl.ItemsControlFromItemContainer method to get a reference to the parent ListView:
public class GridViewEx : GridView
{
public ListView Owner { get; set; }
public GridViewEx()
{
}
protected override void PrepareItem(ListViewItem item)
{
base.PrepareItem(item);
Owner = Owner ?? ItemsControl.ItemsControlFromItemContainer(item) as ListView;
}
}
You can use Binding with RelativeSource and AncestorType, try the code below:
XAML:
<Grid>
<ListView ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
<GridViewColumn Header="Test">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button
DataContext="{Binding Path=ItemsSource, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
Content="Get ListView ItemsSource Count"
Click="Test_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
C#:
class User
{
public string Name { get; set; }
public int Age { get; set; }
public string Mail { get; set; }
}
public MainWindow()
{
InitializeComponent();
DataContext = new List<User>
{
new User() { Name = "John Doe", Age = 42, Mail = "john#doe-family.com" },
new User() { Name = "Jane Doe", Age = 39, Mail = "jane#doe-family.com" },
new User() { Name = "Sammy Doe", Age = 7, Mail = "sammy.doe#gmail.com" }
};
}
private void Test_Click(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement fe)
if (fe.DataContext is List<User> users)
MessageBox.Show($"ItemsSource items count: {users.Count()}.", "ListView Test");
}

how to bind integer value of enum to datagridtextcolumn in wpf

I Want to bind Enum int value to datagridtextboxcolumn.
I use code below
public enum Enm_Purchase_Ret : short
{
Purchase = 1,
Sale = 2,
Return = 3
}
public class Vm_Purchase : INotifyPropertyChanged
{
private Enumitem EnumItem = new Enumitem { Enm_Purchase_Rets = Enm_Purchase_Ret.Purchase };
public Vm_Purchase()
{
}
public class Enumitem
{
public Enm_Purchase_Ret Enm_Purchase_Rets { get; set; }
}
public Enumitem TestenumClass
{
get { return this.EnumItem; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(PropertyName));
}
}
}
In XAML
<DataGridComboBoxColumn Header="Value" ItemsSource="{Binding Source={StaticResource GetEnumValues}, UpdateSourceTrigger=PropertyChanged}" Width="100"
SelectedItemBinding ="{Binding Enm_Purchase_Rets, Mode=TwoWay}" />
<DataGridTextColumn Binding="{Binding xxx}" Header="Enum Id" Width="80" />
Here I want to bind Enum Value ie. 1,2,3 etc in xxx posotion
As I am not much expert in wpf, Please help how to bind this.
Thanks.
DataGridTextColumn displays result of ToString() method call. It is possible to get numeric value of enum by using ToString with format "D". To get the same result with binding add StringFormat:
Binding="{Binding Path=Enm_Purchase_Rets, StringFormat='\{0:D\}'}"

More Binding To ListView

Original question was here.
Binding To A ListView
I have fixed one issue and now see the column names. However, I can't figure out the binding.
The error from the output window:
System.Windows.Data Error: 4 : Cannot find source for binding with reference
'ElementName=This'. BindingExpression:Path=LogView.LogEntries; DataItem=null; target
element is 'ListView' (Name='LoggingListView'); target property is 'ItemsSource' (type 'IEnumerable')
Snippet of the XAML with my latest attempt from LogFileWindow.XAML. I can post more but trying to keep the clutter down:
<ListView Name="LoggingListView" ItemsSource="{Binding ElementName=This, Path=LogView.LogEntries} ">
<ListView.View>
<GridView>
<GridViewColumn Header="Date" DisplayMemberBinding="{Binding Path=Date}"></GridViewColumn>
<GridViewColumn Header="Time" DisplayMemberBinding="{Binding Path=Time}"></GridViewColumn>
<GridViewColumn Header="Event" DisplayMemberBinding="{Binding Path=Event}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The C# ViewModel:
public class LogEntryViewModel : INotifyPropertyChanged
{
public LogEntryViewModel(LogFileEntry le)
{
_date = le.Date;
_time = le.Time;
_event = le.Event;
}
#region Members
private string _date;
public string Date
{
get { return _date; }
set {_date = value;
RaisePropertyChanged("Date");
}
}
private string _time;
public string Time
{
get { return _time; }
set
{
_time = value;
RaisePropertyChanged("Time");
}
}
private string _event;
public string Event
{
get { return _event; }
set { _event = value;
RaisePropertyChanged("Event");
}
}
private LogFileEntry _le;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class LogViewModel :ObservableCollection<LogEntryViewModel>
{
private ObservableCollection<LogEntryViewModel> _LogEntries;
public ObservableCollection<LogEntryViewModel> LogEntries = new
ObservableCollection<LogEntryViewModel>();
}
Partial Class declaration and code-behind where I am trying to use it:
public partial class LogFileWindow : Window
{
public LogViewModel LogView = new LogViewModel();
}
The Visual Studio error is pretty self-evident and self explanatory: you're trying to do Binding with ElementName and searching for a visual element (supposedly defined in your XAML visual tree) with Name="This". There's no such thing (apparently, I couldn't tell because you didn't post the complete XAML tree).
If you want to bind a Visual Element Property to another Property in the same element you have you use RelativeSource Self

WPF ListView setting SelectedItem

I've tried to search for an answer to this but I'm not having any luck. Basically I have a listview that is bound to a collection returned from a view model. I bind the selected item of the list view to a property in my listview in order to perform validation to ensure that an item is selected. The problem is that sometimes I want to load this listview with one of the items already selected. I was hoping to be able to set the property on my view model with the object I want selected and have it automatically select that item. This is not happening. My listview loads without an item selected. I can successfully set the selected index to the 0th index so why shouldn't I be able to set the selected value. The list view is in single selection mode.
Here's the pertinent code from my list view
<ListView Name="listView1" ItemsSource="{Binding Path=AvailableStyles}" SelectionMode="Single">
<ListView.SelectedItem>
<Binding Path="SelectedStyle" ValidatesOnDataErrors="True" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" BindingGroupName="StyleBinding" >
</Binding>
</ListView.SelectedItem>
<ListView.View>
<GridView>
<GridViewColumn Header="StyleImage">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="800.jpg"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Style Code" DisplayMemberBinding="{Binding StyleCode}"/>
<GridViewColumn Header="Style Name" DisplayMemberBinding="{Binding StyleName}"/>
</GridView>
</ListView.View>
</ListView>
And here is the pertinent code from my view model
public class StyleChooserController : BaseController, IDataErrorInfo, INotifyPropertyChanged
{
private IList<Style> availableStyles;
private Style selectedStyle;
public IList<Style> AvailableStyles
{
get { return availableStyles; }
set
{
if (value == availableStyles)
return;
availableStyles = value;
OnPropertyChanged("AvailableStyles");
}
}
public Style SelectedStyle
{
get { return selectedStyle; }
set
{
//if (value == selectedStyle)
// return;
selectedStyle = value;
OnPropertyChanged("SelectedStyle");
}
}
public StyleChooserController()
{
AvailableStyles = StyleService.GetStyleByVenue(1);
if (ApplicationContext.CurrentStyle != null)
{
SelectedStyle = ApplicationContext.CurrentStyle;
}
}
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
string error = string.Empty;
if (columnName == "SelectedStyle")
{
if (SelectedStyle == null)
{
error = "required";
}
}
return error;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
I should note that the "Style" referenced here has nothign to do with WPF. It's a business object. I'm really looking for a solution that doesn't break the MVVM pattern, but I'd be willing to just get something functioning. I've attempted to loop through the Listview.Items list just to set it manually but it's always empty when I try. Any help is appreciated.
Edit: I updated the code to use INotifyPropertyChanged. It's still not working. Any other suggestions
2nd Edit: I added UpdateSourceTrigger="PropertyChanged". That still did not work.
Thanks
Your problem is most likely caused because your SelectedItem Style is a different Style instance than the matching one in the AvailableStyles in the ItemsSource.
What you need to do is provide your specific definition of equality in your Style class:
public class Style: IEquatable<Style>
{
public string StyleCode { get; set; }
public string StyleName { get; set; }
public virtual bool Equals(Style other)
{
return this.StyleCode == other.StyleCode;
}
public override bool Equals(object obj)
{
return Equals(obj as Style);
}
}
Hmm... it looks like you forgot to implement INotifyPropertyChanged for the SelectedStyle property...

Resources