Update rowstyleselector based on propertychange - wpf

I am using RowStyleSelector to color my Datagridrow based on the item. It works fine when the row are drawn, though it does not get triggered when the property changed on the item. The value of the item is shown in the datagrid on change thanks to inotifyPropertyChange.
ListViewFileList.RowStyleSelector= new ErrorStyleSelector();
enter code here
private class ErrorStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is myFile)
{
if ((item as myFile).ErrorStatus.IsErrorfile())
{
Style st = new Style(typeof(DataGridRow));
st.Setters.Add(new Setter(BackgroundProperty, Brushes.Red));
return st;}
}
}
I would like to trigger the RowStyleSelector on item change. Without using xaml.
EDIT
I ended up using IvalueConverter
Style st = new Style(typeof(DataGridRow));
DataTrigger tig = new DataTrigger()
{
Binding = new Binding(nameof(myFile.ErrorStatus))
{
Converter = new ConverterError(),
}
};
st.Triggers.Add(tig);
ListViewFileList.RowStyle = st;
private class ConverterError : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
TRCFile obj = value as myError;
if (obj!= null && othercondition)
{
Style st = new Style(typeof(DataGridRow));
return st;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

The StyleSelector won't create a new style when a PropertyChanged event is raised.
You should make IsErrorfile() a public property and bind to it using a DataTrigger in the Style:
myFile myFile = item as myFile;
if(myFile != null)
{
Style st = new Style(typeof(DataGridRow));
DataTrigger dataTrigger = new DataTrigger()
{
Binding = new Binding("ErrorStatus.IsErrorfile"),
Value = true
};
dataTrigger.Setters.Add(new Setter(BackgroundProperty, Brushes.Red));
st.Triggers.Add(dataTrigger);
return st;
}

Related

Disable Text Wrapping on a Dynamically Filled DataGrid WPF

As the title mentions I am creating a DataGrid as follows:
dataGrid = new DataGrid();
dataGrid.ItemsSource = dSet.Tables[i].DefaultView;
and adding it inside a WrapPanel. Everything is working but I need to disable the text wrapping on rows with multiple lines and limit the column width and having them displaying three dots.
I tried styling the DataGridCell but nothing is working for me.
Can anyone help with this?
Note: I cannot create predefined columns since this is a dynamic grid that I cannot determine before what columns it will populate.
You could handle the AutoGeneratingColumn event and use a converter that converts the long string to a shorter one:
dataGrid = new DataGrid();
dataGrid.ItemsSource = dSet.Tables[i].DefaultView;
LongTextConverter LongTextConverter = new LongTextConverter();
dataGrid.AutoGeneratingColumn += (ss, ee) =>
{
DataGridTextColumn column = ee.Column as DataGridTextColumn;
column.Binding = new Binding(ee.PropertyName) { Converter = LongTextConverter };
};
LongTextConverter.cs:
public class LongTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string s = value as string;
if (!string.IsNullOrEmpty(s) && s.Length > 10)
return $"{s.Substring(0, 7)}...";
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

Bind mui ModernTab wpf mvvm

im rookie with wpf + mvvm, have a simple mui:ModernTab control with items harcoded.
<mui:ModernTab Layout="List" SelectedSource="/Pages/Settings/Appearance.xaml">
<mui:ModernTab.Links>
<mui:Link DisplayName="appearance" Source="/Pages/Settings/Appearance.xaml" />
<mui:Link DisplayName="about" Source="/Pages/Settings/About.xaml" />
</mui:ModernTab.Links>
</mui:ModernTab>
I want populate it tab with the dbdata on the constructor of viewModel something like this on xaml code:
<ScrollViewer>
<mui:ModernTab Layout="List" Links="{Binding AllowedViews}" />
</ScrollViewer>
on viewModel c# constructor as:
public class ApplicationViewModel:ViewModelBase
{
private LinkCollection allowedViews;
public LinkCollection AllowedViews
{
get { return allowedViews; }
set {
allowedViews = value;
NotifyPropertyChanged("tabitem");
}
}
public ApplicationViewModel()
{
allowedViews.Add(new Link() { DisplayName = "item1"});
allowedViews.Add(new Link() { DisplayName = "item2" });
allowedViews.Add(new Link() { DisplayName = "item3" });
}
//allowedViews.Add(new Link() { DisplayName = "Otra Ventana", Source = new Uri("/Views/ModernWindow1.xaml", UriKind.RelativeOrAbsolute) });
}
Questions:
1-is better use a LinkCollection or List to populate data.
The right way to do the binding is with prop Links on xaml?
someone can sahre any documentation or example?
Thanks a lot. excuse my english.
public LinkCollection AllowedViews
{
get { return allowedViews; }
set {
allowedViews = value;
NotifyPropertyChanged("tabitem");
}
}
This "tabitem" should be "AllowedViews", right?
Here is a definition of dynamic links
<mui:ModernTab Layout="List" Links ="{Binding MyIEnumerable, Converter={StaticResource myCollectionToLinksConverter}}">
<mui:ModernTab.ContentLoader>
<app:MyControlLoader />
</mui:ModernTab.ContentLoader>
</mui:ModernTab>
then add a definition of a converter to your window or control
<UserControl.Resources>
<MyCollectionToLinksConverter x:Key="myCollectionToLinksConverter"/>
</UserControl.Resources>
then add the converter class
public class MyCollectionToLinksConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var source = (ICollection<MyCollectionItem>)value;
return new LinkCollection(source.Select(i => new Link() {DisplayName = i.Name, Source = new Uri(v.i, UriKind.Relative)}));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
then add your content Loader
class MyControlLoader: DefaultContentLoader
{
protected override object LoadContent(Uri uri)
{
var myTarget = UIModel.Instance.GetMyTargetObjectById(v => v.Name == uri.OriginalString);
return new YourTabContentControl() {DataContext = myTarget};
}
}

Binding the Path Property of a Binding

is it possible to bind the Path property of a binding to another property?
I want to realize this code:
Text="{Binding Path={Binding Path=CurrentPath}}"
So I can adjust dynamically to which Property my actual binding is refering.
Thanks for your Help
Jonny
I worked it out on myself.
Heres the solution, I hope it might help anyone got the same problem like me.
public class CustomBindingBehavior : Behavior<FrameworkElement>
{
public bool IsBinding
{
get
{
return (bool)GetValue(IsBindingProperty);
}
set
{
SetValue(IsBindingProperty, value);
}
}
public string PropertyPath
{
get
{
return (string)GetValue(PropertyPathProperty);
}
set
{
SetValue(PropertyPathProperty, value);
}
}
public static DependencyProperty
PropertyPathProperty = DependencyProperty.Register("PropertyPath", typeof(string),
typeof(CustomBindingBehavior), null);
public static DependencyProperty
IsBindingProperty = DependencyProperty.Register("IsBinding", typeof(bool),
typeof(CustomBindingBehavior), null);
protected override void OnAttached()
{
if (AssociatedObject is TextBlock)
{
var tb = AssociatedObject as TextBlock;
tb.Loaded += new RoutedEventHandler(tb_Loaded);
}
}
private void tb_Loaded(object sender, RoutedEventArgs e)
{
AddBinding(sender as TextBlock, TextBlock.TextProperty);
}
private void AddBinding(DependencyObject targetObj, DependencyProperty targetProp)
{
if (IsBinding)
{
Binding binding = new Binding();
binding.Path = new PropertyPath(this.PropertyPath, null);
BindingOperations.SetBinding(targetObj, targetProp, binding);
}
else
{
targetObj.SetValue(targetProp, this.PropertyPath);
}
}
}
And heres the implementation in XAML:
<TextBlock >
<i:Interaction.Behaviors>
<behaviors:CustomBindingBehavior PropertyPath="{Binding Path=HeaderPropertyBinding}" IsBinding="{Binding Path=HeaderIsBinding}" />
</i:Interaction.Behaviors>
</TextBlock>
Greetings
Jonny
As other posters have mentioned, you can only set a binding on a dependency property - which path is not. The underlying reason is that xaml is source code that gets compiled. At compile time the compiler has no idea what the value of 'CurrentPath' is, and would not be able to compile. Essentially what you are looking to do is runtime reflection of a property value - which could be done using another property in the ViewModel you are binding to, or using a converter.
ViewModel:
public string CurrentValue
{
get
{
var property = this.GetType().GetProperty(CurrentPath);
return property.GetValue(this, null);
}
}
Using a converter:
public class CurrentPathToValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var viewModel = (ViewModel)value;
var property = viewModel.GetType().GetProperty(viewModel.CurrentPath);
var currentValue = property.GetValue(viewModel, null);
return currentValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Of couse these only work if you want to get a simple property of the object - if you want to get something more complex your reflection code is going to get a lot more complex.
Unless you are building something like a property grid, or for some other reason you actually want to introspect the objects running in your application, I would suggest you revisit your design, as reflection is really only suited to a few situations.
Path is not a dependency property, therefore the binding will not work.
Perhaps you could bind to a property that returns another property based on a switch statement and bind to that. Change the 'switch' property and you change the output of the other property.
Just don't forget to include your NotifyPropertyChanged stuff in the switch property for the bound property otherwise your view will not update.
e.g.
private int _mySwitch;
//Set this to determine what the other property will return.
public int SwitchProperty
{
get { return _mySwitch; }
set
{
_mySwitch = value;
NotifyPropertyChanged("MySwitchableProperty");
}
}
public String PropertyA { get; set; }
public String PropertyB { get; set; }
//Bind to this property
public String MySwitchableProperty
{
get
{
switch (SwitchProperty)
{
case 1:
return PropertyA;
break;
case 2:
return PropertyB;
break;
default :
return String.Empty;
break;
}
}
}
I think converter can helps your.
Expample
First control
Text="{Binding Path=CurrentPath}"
Second control
Text="{Binding Path=CurrentPath, Convertor={converters:MyConvertor}}"
Base converter
public abstract class ConvertorBase<T> : MarkupExtension, IValueConverter
where T : class, new()
{
public abstract object Convert(object value, Type targetType, object parameter,
CultureInfo culture);
public virtual object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
#region MarkupExtension members
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
_converter = new T();
return _converter;
}
private static T _converter = null;
#endregion
}
MyConverter
public class MyConverter: ConvertorBase<MyConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (string)value.Equals("blabla") ? "Yes" : "No"; // here return necessary parametr
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}

DependencyProperty not firing

I have the following XAML that uses a classes CurrencyImg property to look up a static resource at runtime, as there are a lot of these in a treeview, I don't want to have to load the same image 1000 times, I keep it in a resourcedictionary so it is only loaded once.
<Image Name="imgCurrency" Grid.Column="5" Margin="0,0,0,0" Source="{w:ImageStaticResource {Binding CurrencyImg}}" Height="22" VerticalAlignment="Top"/>
With a sample resource dictionary entry as
<ImageSource x:Key="..\Resources\Images\USD.ico">../Resources/Images/USD.ico</ImageSource>
The property is as follows
public string CurrencyImg
{
get
{
if (DisplayUSDRate)
{
return AppString.General.ImagePath + AppString.CurrencyId.USD + ".ico";
}
else
{
return AppString.General.ImagePath + curr + ".ico";
}
}
}
And the property action which does the work
public override bool DisplayUSDRate
{
get { return _customer.DisplayUSDRate; }
set
{
_customer.DisplayUSDRate = value;
OnPropertyChanged("CurrencyImg");
}
}
Now when this runs, the OnPropertyChanged("CurrencyImg") does nothing, and it's clear why. In the XAML I'm not directly binding to the CurrencyImg property, I'm using it as a parameter to my StaticResourceExtension class, so when I call OnPropertyChanged, it thinks there are no bound properties to update, so the XAML image does not update. I can understand this, but obviously this does not help me as what I need to happen is the following.
1) For the class to determine which currency image gets displayed for that row
2) For the images to come from a resource dictionary so they don't get loaded more than once or performance hugely suffers
3) For changes to the DisplayUSDRate property to then reflect by updating the flag to the appropriate image. This is the bit I can't figure out how to do with my StaticResourceExtension (code below)
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Citi.Rbcs.UI.Windows
{
public class ImageStaticResource : StaticResourceExtension
{
public Binding Binding { get; set; }
private static readonly DependencyProperty DummyProperty;
public ImageStaticResource()
{
}
public ImageStaticResource(Binding binding)
{
Binding = binding;
}
static ImageStaticResource()
{
DummyProperty = DependencyProperty.RegisterAttached(
"Dummy",
typeof (Object),
typeof (DependencyObject),
new UIPropertyMetadata(null));
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var targetObject = (FrameworkElement)target.TargetObject;
Binding.Source = targetObject.DataContext;
var DummyDO = new DependencyObject();
BindingOperations.SetBinding(DummyDO, DummyProperty, Binding);
ResourceKey = DummyDO.GetValue(DummyProperty);
var resourceDictionary = new ResourceDictionary
{
Source = new Uri("pack://application:,,,/Windows/Images.xaml")
};
var key = (string) ResourceKey;
if (!resourceDictionary.Contains(key)) ResourceKey = "Default";
return base.ProvideValue(serviceProvider);
}
}
}
You could write a very simple binding converter that converts image URI strings to cached ImageSource objects:
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var imageUrl = value as string;
var image = MemoryCache.Default.Get(imageUrl) as ImageSource;
if (image == null)
{
image = new BitmapImage(new Uri(imageUrl));
MemoryCache.Default.Set(imageUrl, image, new CacheItemPolicy());
}
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Bind a Silverlight TabControl to a Collection

I have a Collection of Model-objects in my ViewModel. I would like to be able to bind a TabControl to these and use a DataTemplate to extract the information from the Model-objects. When I try to do this I get the errormessage: Unable to cast object of type Model to object of type TabItem. After spending some time looking for a solution I found the following:
The Silverlight TabControl is
broken. Use a combination of ListBox
and ContentControl to mimic the
behaviour of a TabControl. (Means
that I have to skin the ListBox to
look like a TabControl)
TabControl does not override
PrepareContainerForItemOverride and
the solution is to make a
Converter. (Not so good because I
then need to specify the type of the
convertee in the Converter)
Anyone know any better solution?
XAML
<sdk:TabControl ItemsSource="{Binding Items, ElementName=MyControl}">
<sdk:TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</sdk:TabControl.ItemTemplate>
</sdk:TabControl>
C#
public ObservableCollection<Model> Items { get; set; }
public ViewModel()
Items = new ObservableCollection<Model>{
new Model { Name = "1"},
new Model { Name = "2"},
new Model { Name = "3"},
new Model { Name = "4"}
};
}
Suggested Converter:
public class TabConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<TabSource> source = value as List<TabSource>;
if (source != null)
{
List<TabItem> result = new List<TabItem>();
foreach (TabSource tab in source)
{
result.Add(new TabItem()
{
Header = tab.Header,
Content = tab.Content
});
}
return result;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Create converter
public class SourceToTabItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
var source = (IEnumerable)value;
if (source != null)
{
var controlTemplate = (ControlTemplate)parameter;
var tabItems = new List<TabItem>();
foreach (object item in source)
{
PropertyInfo[] propertyInfos = item.GetType().GetProperties();
//тут мы выбираем, то поле которое будет Header. Вы должны сами вводить это значение.
var propertyInfo = propertyInfos.First(x => x.Name == "name");
string headerText = null;
if (propertyInfo != null)
{
object propValue = propertyInfo.GetValue(item, null);
headerText = (propValue ?? string.Empty).ToString();
}
var tabItem = new TabItem
{
DataContext = item,
Header = headerText,
Content =
controlTemplate == null
? item
: new ContentControl { Template = controlTemplate }
};
tabItems.Add(tabItem);
}
return tabItems;
}
return null;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// ConvertBack method is not supported
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("ConvertBack method is not supported");
}
Create ControlTemplate:
<ControlTemplate x:Key="MyTabItemContentTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=name}" />
</StackPanel>
</ControlTemplate>
And binding convert, controltemplate
<controls:TabControl x:Name="tabControl"
ItemsSource="{Binding ElementName=tabControl,
Path=DataContext,
Converter={StaticResource ConverterCollectionToTabItems},
ConverterParameter={StaticResource MyTabItemContentTemplate}}">
</controls:TabControl>
taken from the blog binding-tabcontrol

Resources