How converter null to ImageNull.jpg - silverlight

This code not work?
column don't show
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Tag="{Binding photo}" MaxHeight="50">
<Image.Source>
<BitmapImage UriSource="{Binding photo, Converter={StaticResource ConvertNullImageKey}}" />
</Image.Source>
</Image>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
Converter:
public class ConvertNullImage : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var image = new BitmapImage();
try
{
image = new BitmapImage(new Uri(value.ToString()));
return image;
}
catch { return new BitmapImage(new Uri("http://upload.wikimedia.org/wikipedia/commons/1/1c/No-Symbol.png")); }
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}

What you are currently doing is taking an existing BitmapImage and trying to assign another BitmapImage to its UriSource. Have you tried this:-
<Image Tag="{Binding photo}" MaxHeight="50" Source="{Binding photo, Converter={StaticResource ConvertNullImageKey}}" />

Related

WPF textbox expression bindings

I have two textboxes bound to a slider. I want the second textbox to be 1.2 time the value of the slide.
<Slider Maximum="500" Minimum="25" TickFrequency="5" IsSnapToTickEnabled="True" Name="slCellHeight" />
<TextBox Text="{Binding ElementName=slCellHeight, Path=Value, UpdateSourceTrigger=PropertyChanged}" Width="40" Name="txtCellHeight" />
<TextBox Text="{Binding ElementName=slCellHeight, Path=Value, UpdateSourceTrigger=PropertyChanged}" Width="40" Name="txtCellWidth" />
That is, when the slider shows 100, the first textbox(txtCellHeight) should show 100. This is working fine. I want to the second one to be 120.
I tried the calBinding but no success. Please suggest some good ways to do that.
Use a converter.
XAML:
<Window.Resources>
<local:MultiplyConverter x:Key="MultiplyConverter" />
</Window.Resources>
...
<TextBox Text="{Binding ElementName=slCellHeight, Path=Value,
UpdateSourceTrigger=PropertyChanged, Converter={StaticResource
MultiplyConverter}, ConverterParameter=1.2 }" Width="40" Name="txtCellWidth" />
Class MultiplyConverter:
class MultiplyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double result;
double.TryParse(parameter.ToString(), out result);
double mult = (double)value * result;
return mult;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I've done it on the fly, so maybe it will need some fixes to compile.

DataGrid Items collection doesn't always refresh

I have a DataGrid bound to an ObservableCollection<Client>.
I have a UserControl which is responsible to apply a filter on the collection from a Textbox value like this:
private void UCFilterBox_SearchTextChanged(object sender, string e)
{
var coll = CollectionViewSource.GetDefaultView(dgClients.ItemsSource);
coll.Filter = o =>
{
var c = o as Client;
if (c != null)
{
bool ret = (the filter...)
return ret;
}
else
{
return false;
}
};
}
Then I have a TextBlock which is bound to the DataGrid's Items collection like this:
<StackPanel Grid.Row="0"
Margin="215,0,0,5"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<TextBlock Style="{StaticResource SmallTextBlockStyle}" Text="{Binding ElementName=dgClients, Path=Items.Count}" />
<TextBlock Style="{StaticResource SmallTextBlockStyle}"
Text="{Binding ElementName=dgClients, Path=Items.Count, Converter={StaticResource ClientSingleOrPluralConverter}, StringFormat={} {0}}" />
</StackPanel>
This is working correctly, and each time the DataGrid is filtered, the value changes accordingly.
However, I have another TextBlock bound to the DataGrid's Items collection, which is responsible of showing the sum of the displayed data, and this one is not updating !
<TextBlock Margin="5"
FontWeight="Bold"
Text="{Binding ElementName=dgClients,
Path=Items,
Converter={StaticResource CalculateSumConvertor},
StringFormat={}{0:C}}" />
The CalculateSumConvertor is only hit once at the binding of the DataGrid and then no more.
Here is the converter:
public class CalculateSumConvertor: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var clients = value as ItemCollection;
if (clients != null)
{
return clients.Cast<Client>().Sum(c => c.FieldToSum);
}
else
{
return DependencyProperty.UnsetValue;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Is there something I'm doing wrongly here ?
Change your binding to the following and change your converter to an IMultiValueConverter
<TextBlock Margin="5"
FontWeight="Bold">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource CalculateSumConvertor}" StringFormat="{}{0:C}">
<Binding ElementName="dgClients" Path="Items" />
<Binding ElementName="dgClients" Path="Items.Count" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Converter:
class CalculateSumConvertor : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var clients = values[0] as ItemCollection;
...
}
}

Disable scrolling if the cursor is over element

I wanted to disable the scrolling when the cursor is over an element in WPF.
If the mouse is over the red rectangle, the scroll should be disabled.
The red rectangle is on a scrollviewer.
Any idea?
I got it to work with this code:
<Window.Resources>
<converter:MouseOverToScrollBarVisibility x:Key="scrollVisibility" />
</Window.Resources>
<ScrollViewer VerticalScrollBarVisibility="{Binding IsMouseOver,
ElementName=rec,
Converter={StaticResource scrollVisibility}}">
<Rectangle Height="50" Width="50" Fill="Red" x:Name="rec"/>
</ScrollViewer>
Then just define this converter:
[ValueConversion(typeof(bool), typeof(ScrollBarVisibility))]
sealed class MouseOverToScrollBarVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value) ? ScrollBarVisibility.Hidden : ScrollBarVisibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}

Binding a Distinction basedon Actual Width to Visibility

Is it possible to bind an ActualWidth or Width property of a Control to the Visibility of another Control with a distinction about value (like <200)? In my Opinion it is only possible with a converter because a DataTrigger can not work with > or <.
So I tried it with a Converter but it didn't work. I'm not sure which BindingMode is necessary and which kind of converter I need for such a solution.
The xaml code:
<StackPanel>
<Slider x:Name="slider" Height="36" Width="220" Maximum="500"/>
<Rectangle x:Name="mover" Height="12" Stroke="Black" Width="{Binding Value, ElementName=slider}"/>
<Rectangle x:Name="rectangle" Fill="#FFFF9E0E" Height="34" Width="112" Visibility="{Binding ActualWidth, Converter={StaticResource umkehr}, ElementName=rectangle, Mode=OneWay}"/>
</StackPanel>
and the idea for the converter:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null) {
var val = System.Convert.ToDouble(value);
if (val > 100)
return Visibility.Visible;
return Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
It is likely not working because you are binding your Rectangle's Visibility to the same rectangle's ActualWidth, and an invisible rectangle will always have a width of 0, so will never be visible.
Here's some examples. One binds to the other rectangle's ActualWidth, and the other binds to your Slider's Value
<Rectangle x:Name="rectangle"
Visibility="{Binding ActualWidth, ElementName=mover,
Converter={StaticResource umkehr}}"/>
or
<Rectangle x:Name="rectangle"
Visibility="{Binding Value, ElementName=slider,
Converter={StaticResource umkehr}}"/>
And as far as I know, there's no easy way of basing a value off of if something is greater than or less than a value. Coverters are your best option.
ActualWidth is a readonly property exposed by FrameworkElement class -
public double ActualWidth { get; }
It is get only property hence you can't set it to other value from code. You can bind to Width of your control instead to make it work.
EDIT
This works for me, may be this is what you want -
Converter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double)
{
return ((double)value > 100) ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
XAML
<StackPanel>
<Slider x:Name="slider" Height="36" Width="220" Maximum="500"/>
<Rectangle x:Name="mover" Height="12" Stroke="Black" Width="{Binding Value, ElementName=slider}"/>
<Rectangle x:Name="rectangle" Fill="#FFFF9E0E" Height="34" Width="112" Visibility="{Binding ActualWidth, Converter={StaticResource MyConverter}, ElementName=mover, Mode=OneWay}"/>
</StackPanel>
If you're attempting to change the Visibility of a control based on the ActualWidth of another control, you'll either need to use a IValueConverter or you're own type of MarkupExtension (inherit from Binding or BindingBase).
Converter Option:
[ValueConversion(typeof(Double), typeof(Visibility))]
[ValueConversion(typeof(Double?), typeof(Visibility))]
public class MinimumLengthToVisibilityConverter : IValueConverter
{
public Double MinLength { get; set; }
public override Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
{
if ((value == null) || !(value is Double))
{
return DependencyProperty.UnsetValue;
}
return (((Double)value) > MinLength) ? Visibility.Visible : Visibility.Collapsed;
}
public override Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
MarkupExtension Option:
Read this blog post to get a better feel for how to implement this...
You can actually have the value in a parameter, so you can re-use the converter if you need to:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double cutoff = 0.0;
if(parameter is double)
{
cutoff = (double)parameter;
}
if (parameter is string)
{
Double.TryParse(parameter.ToString(), out cutoff);
}
if (value is double)
{
return ((double)value > cutoff) ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
And the XAML:
<StackPanel>
<Slider x:Name="slider" Height="36" Width="220" Maximum="500"/>
<Rectangle x:Name="mover" Height="12" Stroke="Black" Width="{Binding Value, ElementName=slider}"/>
<Rectangle x:Name="rectangle" Fill="#FFFF9E0E" Height="34" Width="112"
Visibility="{Binding ActualWidth, Converter={StaticResource ActualWidthToVisibilityConverter},
ElementName=mover, Mode=OneWay, ConverterParameter=100}"/>
</StackPanel>

WPF ComboBox a better way to format ItemsSource

Morning Guys,
I have a few ComboBoxes bound to List of TimeSpan. I am formatting the TimeSpans using IValueConverter and ItemTemplate. I was wondering if there were an easier way to format the TimeSpans. Here's what I'm currently doing.
public class TimeSpanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return string.Empty;
TimeSpan t = TimeSpan.MinValue;
TimeSpan.TryParse(value.ToString(), out t);
return "{0:00}:{1:00}".F(t.Hours,t.Minutes);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return TimeSpan.Parse(value.ToString());
}
#endregion
}
<Canvas>
<Canvas.Resources>
<bc:TimeSpanConverter x:Key="ts" />
<DataTemplate x:Key="TimeSpanTemplate">
<TextBlock Text="{Binding ., Converter={StaticResource ts}}" />
</DataTemplate>
</Canvas.Resources>
<TextBlock Canvas.Left="6"
Canvas.Top="6"
Height="21"
Name="textBlock4"
Text="Begin"
Width="40" />
<TextBlock Canvas.Left="81"
Canvas.Top="6"
Height="21"
Name="textBlock5"
Text="End"
Width="40" />
<ComboBox Canvas.Left="7"
Canvas.Top="25"
Height="23"
Name="TimeBeginCombo"
ItemTemplate="{StaticResource TimeSpanTemplate}"
SelectedItem="{Binding TimeBegin}"
Width="68" />
<ComboBox Canvas.Left="81"
Canvas.Top="25"
Height="23"
Name="TimeEndCombo"
ItemTemplate="{StaticResource TimeSpanTemplate}"
SelectedItem="{Binding TimeEnd}"
Width="68" />
</Canvas>
</GroupBox>
Is what you're binding to of type TimeSpan? If so, then the converter can be much simpler
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is TimeSpan))
return string.Empty;
TimeSpan t = (TimeSpan)value;
return "{0:00}:{1:00}".F(t.Hours,t.Minutes);
}
And also, a general remark - are you sure you need to layout your UI on a Canvas and using absolute coordinates? :)

Resources