Reversed Listbox without sorting - wpf

I spent last two weeks trying to figure out a method to display the items of a listbox in reversed order without using any sort property and without putting any presentation logic in my entities, I just want that the last inserted item is displayed at the top of the listbox.
The only pure XAML solution I've found is this WPF reverse ListView but it isn't so elegant. I've also tried to override the GetEnumerator() of my BindableCollection (I use Caliburn Micro as MVVM framework) to return an enumerator that iterate over my collection's items in the reverse order but id did not work.
How can I do?

ScaleTransform is an elegant solution to this particular case, but there could be more generic applications (such as binding the same list but with different permutations applied).
It is possible to do this all with converters if you make sure to consider that the binding is to the list, rather than the elements of the list. Assuming that your using an ObservableCollection of strings (sure it would be possible to use generics and reflection to make this more elegant) and missing out all the proper coding of exception handling and multiple calls to Convert...
public class ReverseListConverter : MarkupExtension, IValueConverter
{
private ObservableCollection<string> _reversedList;
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
_reversedList = new ObservableCollection<string>();
var data = (ObservableCollection<string>) value;
for (var i = data.Count - 1; i >= 0; i--)
_reversedList.Add(data[i]);
data.CollectionChanged += DataCollectionChanged;
return _reversedList;
}
void DataCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
var data = (ObservableCollection<string>)sender;
_reversedList.Clear();
for (var i = data.Count - 1; i >= 0; i--)
_reversedList.Add(data[i]);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
You can then bind within your XAML with something like
<ListBox ItemsSource="{Binding Converter={ReverseListConverter}}"/>

Related

Hiding columns from datagrid when autogenerate=false

I create columns in codebehind as such.. each column is bound to a property.
Dim column_selected As New DataGridCheckBoxColumn()
column_selected.Header = "Selected"
column_selected.Binding = New Binding("IsChecked")
dgvResults.Columns.Add(column_selected)
I want to be able to hide a column, based on a checkbox or something of that nature, where I can hide/show them at will.
I've heard about binding visibility to a property Boolean, but i'm not sure how to do that when the columns are created in code behind.
Any idea on how to accomplish this? Say I want to have a single checkbox that hides a specific column, if you unchecked it, it shows it.
If logic of setting column visibility doesn't contains any business logic and this is clear UI operation. Then I think you can just put it in the code-behind, what is wrong with that?
XAML
<CheckBox Checked="HideColumn" Unchecked="UnhideColumn"/>
Code-behind
Protected Sub HideColumn()
'your code
End Sub
Protected Sub UnhideColumn()
'your code
End Sub
You can set Binding normally from code-behind, but since System.Visibilty is enum, you have to use Converter (an instance of IValueConverter interface) to set Binding correctly. There are a lots of possible implementation, the following is an example:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? vis = value as bool?;
return (vis.HasValue && vis.Value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
After that, the only thing you should do is to set the Converter property of your Binding to the new instance of BoolToVisibilityConverter as follows:
column_selected.Binding.Converter = new BoolToVisibilityConverter()
And that's all.

Silverlight UI not updating - ObservableCollection being reinstantiated

I'm pretty new to ObservableCollections, but have built some code which I'm sure should work. Unfortunately it doesn't. The only thing that is not happening, is my GUI is not being updated. I know the values are being updated in the back (Checked using Debugger).
What am I doing wrong?
Here with a sample of my XAML for the Textblock:
<TextBlock Name="tbCallsOpen" Text="{Binding IndicatorValue}" />
Herewith sample of my code behind:
public partial class CurrentCalls : UserControl
{
Microsoft.SharePoint.Client.ListItemCollection spListItems;
ObservableCollection<CurrentCallIndicator> CallIndicators = new ObservableCollection<CurrentCallIndicator>();
public CurrentCalls()
{
InitializeComponent();
DispatcherTimer dispatchTimer = new DispatcherTimer();
dispatchTimer.Interval = new TimeSpan(0, 0, 20);
dispatchTimer.Tick += new EventHandler(BindData);
dispatchTimer.Start();
}
private void BindData(object sender, EventArgs args)
{
//splistitems is a sharepoint list. Data is being retrieved succesfully, no issues here.
foreach (var item in spListItems)
{
//My custom class which implements INotifyPropertyChanged
CurrentCallIndicator indicator = new CurrentCallIndicator();
indicator.IndicatorValue = item["MyValueColumn"];
//Adding to ObservableCollection
CallIndicators.Add(indicator);
}
//Setting Datacontext of a normal TextBlock
tbCallsOpen.DataContext = CallIndicators.First(z => z.IndicatorName == "somevalue");
}
}
You are most likely assuming that changes to the underlying items in the collection will raise the CollectionChanged event; however that is not how the ObservableCollection<T> works.
If you wanted this behavior you would need to roll your own implmentation and when a PropertyChanged event is fired within an item within your collection, you would then need to fire the CollectionChanged event.
Your code looks more-or-less correct to me, at first blush - though I wouldn't expect that you'd need to use an ObservableCollection<> to get the results you seem to be expecting: a simple List<> would work just fine.
If the debugger tells you that the DataContext is being updated correctly to the expected item, then the most likely issue is that there's a problem with how your binding is defined. If you're not seeing any binding errors reported in your debug window, then I'd look into Bea Stollnitz' article on debugging bindings. Most specifically, I often use the technique she suggests of a "DebugValueConverter", e.g.:
/// <summary>
/// Helps to debug bindings. Use like this: Content="{Binding PropertyName, Converter={StaticResource debugConverter}}"
/// </summary>
public class DebugConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
And then set a breakpoint in your converter, and watch what's happening. It's a hack and a kludge, but until we're all on SL5 (which has binding debugging built in), it's your best bet.
Ok, Sorted. I fixed the issue myself. Because I was updating the values in a loop, the ObservableCollection wasn't being updated properly. All I did in the beginning of the databinding method, was to Clear the collection : CallIndicators.Clear();

Using Resourse.resx in WPF application to set color

I´m trying to make an application that has to be able to easily change a dll file which could change colors in the application.
I´m trying to use resource manager to do this but am having problems with setting color values so that the styles for views can easily accept it.
We know that(in this case) the background of a button takes in SolidColorBrush, and while
Value="Black" works,
Value={x:Static res:AppResources.Btn_Background}
which gives the string Black does not (current theory being that converters make the former work but not the latter).
This is all being done in wpf & mvvm.
Have you guys an idea about how this could be done.
Greetings
You could use a Binding:
Background="{Binding Source={x:Static res:AppResources.Btn_Background}}"
This will cause the CoerceValue to fire for the DependencyProperty controlling the background.
#Snowbear mentioned it may be a Color rather than a String, in which case you would need to provide a trivial IValueConverter.
public class ColorConverter: IValueConverter
{
#region IValueConverter Members
private Dictionary<Color, Brush> brushes = new Dictionary<Color, Brush>();
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
Brush brush;
var color = (Color)value;
if (!brushes.TryGetValue(color, out brush))
{
brushes[color] = brush = new SolidColorBrush(color);
brush.Freeze();
}
return brush;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Your specific issue is that you are bypassing the default string to Brush conversion and would need to handle that manually.
As sixlettervariables states, you'd can use a Binding if your source is a string, but that is probably overkill. At a minimum, you'd want to set Mode=OneTime on the Binding.
You can also create a custom MarkupExtension that performs the conversion.
Your conversion, whether it be through a custom IValueConverter or MarkupExtension, can leverage the BrushConverter class. So things like "Black" or "#000" will work as they do when defining the color in XAML like your first example.
EDIT:
Actually a markup extension that derives from StaticExtension, makes this easier:
public class BrushStaticExtension : StaticExtension {
private static BrushConverter converter = new BrushConverter();
public BrushStaticExtension() { }
public BrushStaticExtension(string member) : base (member) { }
public override object ProvideValue(IServiceProvider serviceProvider) {
return converter.ConvertFrom(base.ProvideValue(serviceProvider));
}
}
If you specify a string then XAML parser uses a converter from string which automatically creates a SolidColorBrush. As far as I understand at the moment Btn_Background resource is Color but it should be a SolidColorBrush instead.

How can I make a WPF TreeView data binding lazy and asynchronous?

I am learning how to use data binding in WPF for a TreeView. I am procedurally creating the Binding object, setting Source, Path, and Converter properties to point to my own classes. I can even go as far as setting IsAsync and I can see the GUI update asynchronously when I explore the tree. So far so good!
My problem is that WPF eagerly evaluates parts of the tree prior to them being expanded in the GUI. If left long enough this would result in the entire tree being evaluated (well actually in this example my tree is infinite, but you get the idea). I would like the tree only be evaluated on demand as the user expands the nodes. Is this possible using the existing asynchronous data binding stuff in the WPF?
As an aside I have not figured out how ObjectDataProvider relates to this task.
My XAML code contains only a single TreeView object, and my C# code is:
public partial class Window1 : Window
{
public Window1() {
InitializeComponent();
treeView.Items.Add( CreateItem(2) );
}
static TreeViewItem CreateItem(int number)
{
TreeViewItem item = new TreeViewItem();
item.Header = number;
Binding b = new Binding();
b.Converter = new MyConverter();
b.Source = new MyDataProvider(number);
b.Path = new PropertyPath("Value");
b.IsAsync = true;
item.SetBinding(TreeView.ItemsSourceProperty, b);
return item;
}
class MyDataProvider
{
readonly int m_value;
public MyDataProvider(int value) {
m_value = value;
}
public int[] Value {
get {
// Sleep to mimick a costly operation that should not hang the UI
System.Threading.Thread.Sleep(2000);
System.Diagnostics.Debug.Write(string.Format("Evaluated for {0}\n", m_value));
return new int[] {
m_value * 2,
m_value + 1,
};
}
}
}
class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
// Convert the double to an int.
int[] values = (int[])value;
IList<TreeViewItem> result = new List<TreeViewItem>();
foreach (int i in values) {
result.Add(CreateItem(i));
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new InvalidOperationException("Not implemented.");
}
}
}
Note: I have previously managed to do lazy evaluation of the tree nodes by adding WPF event handlers and directly adding items when the event handlers are triggered. I'm trying to move away from that and use data binding instead (which I understand is more in spirit with "the WPF way").
A generic solution (as I'm not sure if your code is not mock)
Create your model containing parent and children (In this case it is an int and a list of int)
Create a ViewModel having a property IsExpanded in addition to the Model's properties
Bind your IsExpanded property to the TreeViewItem's IsExpanded property in the view(xaml)
In the IsExpanded property's setter, fill in your Children list using the Dispatcher, the priority being Background. Each addition of item into your Children List should trigger the PropertyChanged event.
Check out the MVVM design pattern, if you're not familiar with. Here is a good video by Jason

How do I update an IValueConverter on CollectionChanged?

Here's a basic example to explain my problem. Let's say I have
ObservableCollection<int> Numbers {get; set;}
and an IValueConverter that returns the sum of Numbers.
Normally what I'd do is changed the IValueConverter into an IMultiValueConverter and bind a second value to Numbers.Count like this
<MultiBinding Converter="{StaticResource SumTheIntegersConverter}">
<Binding Path="Numbers" />
<Binding Path="Numbers.Count" />
</MultiBinding>
However I'm unable to use this method to solve my actual problem. It seems like there should be a better way to update the binding when the collection changes that I'm just not thinking of. What's the best way to get the value converter to run when items are added and removed to Numbers?
This is actually surprisingly very difficult. An IValueConverter doesn't update, so this does not work as you'd hope.
I wrote a sample on the Microsoft Expression Gallery called Collection Aggregator that shows a working, if convoluted, approach to making this work via a Behavior that does the aggregation (Count, in your case, although I also support Sum, Average, etc) for you, instead of a converter.
In your model, subscribe to CollectionChanged and raise PropertyChanged:
Numbers.CollectionChanged += (o,e) =>
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Numbers)));
And, as thomasgalliker mentioned, you should unsubscribe from the event when the model containing the connection is no longer used.
I ended up doing something like this which seems to work. It's far from an optimal solution and I'd still be interested in something better but it seems to work for my purposes.
class CollectionChangedHandlingValueConverter : IValueConverter
{
DependencyObject myTarget;
DependencyProperty myTargetProperty;
//If this ever needs to be called from XAML you can make it a MarkupExtension and use ProvideValue to set up the Target and TargetProperty
public CollectionChangedHandlingValueConverter(DependencyObject target, DependencyProperty dp)
{
myTarget = target;
myTargetProperty = dp;
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
INotifyCollectionChanged collection = value as INotifyCollectionChanged;
if (collection != null)
{
//It notifies of collection changed, try again when it changes
collection.CollectionChanged += DataCollectionChanged;
}
//Do whatever conversions here
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if ((myTarget != null) && (myTargetProperty != null))
{
BindingOperations.GetBindingExpressionBase(myTarget, myTargetProperty).UpdateTarget();
}
}
}
And I ended up synchronizing collection (original with converter), take a look at the buttom of my post for example:
http://alexburtsev.wordpress.com/2011/03/05/mvvm-pattern-in-silverlight-and-wpf/

Resources