Dynamically switching between canvases wpf - wpf

I have multiple canvas images of different types (image source, geometry, path) and wish to only show 1 depending on a string binding.
whats the best way to do this?
i'd like it to be reusable so i can place this code inside a user control and then have many of these images around the app and i select which 1 is shown.
Like so:
<CanvasImage Image="Pie"/>
<CanvasImage Image="Dog"/>
Would it be too computationally expensive to have them all declared in the user control view and use visibility bindings
Pie canvas example:
<canvas>
<Data ="m24,98,07">
</canvas>
Dog canvas example:
<canvas>
<image source="">
<canvas>

This converter return an image source directly, depending on the value it receives.
namespace TestTreeView.Views
{
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string file = "";
string v = value as string;
switch (v)
{
case "Pie":
file = #".\path\to\your\pie.jpg";
break;
case "Dog":
file = #".\path\to\your\dog.jpg";
break;
default:
return null;
}
return new BitmapImage(new Uri(file, UriKind.RelativeOrAbsolute));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Usage in XAML:
<Window xmlns:local="clr-namespace:YourNamespace.Views" ...>
<Window.Resources>
<local:StringToImageConverter x:Key="stringToImageConverter"/>
</Window.Resources>
<Grid>
<Canvas>
<Image Source="{Binding YourString, Converter={StaticResource stringToImageConverter}}"/>
</Canvas>
</Grid>
</Window>
Original answer
I think you need to use a Converter.
It will take a ConverterParameter, a String, that will tell what the binded value is expected to be, and return a Visiblity to indicate if the canvas should be visible or not.
namespace YourNamespace.Views
{
public class StringToCanvasVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string v = value as string;
string p = parameter as string;
return (v != null && p != null && v == p) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Usage in XAML:
<Window xmlns:local="clr-namespace:YourNamespace.Views" ...>
<Window.Resources>
<local:StringToVisibilityConverter x:Key="stringToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Canvas Visibility="{Binding YourString, Converter={StaticResource stringToVisibilityConverter}, ConverterParameter=Pie}"/>
<Canvas Visibility="{Binding YourString, Converter={StaticResource stringToVisibilityConverter}, ConverterParameter=Dog}"/>
</Grid>
</Window>

Related

Is there a way to make this image binding more performant?

I am binding a list of 500 odd nicks to a list with a status image for each. The scrolling of the list is painfully slow, and so is flicking between tabs with different lists.
This is all caused by my recent change in which I added these images.
Is there a way to speed it up?
My bitmaps (very small 16*16) :
<BitmapImage x:Key="ActiveIcon" UriSource="/WPFClient;component/Images/active.png" />
<BitmapImage x:Key="IdleIcon" UriSource="/WPFClient;component/Images/idle.png" />
<BitmapImage x:Key="AwayIcon" UriSource="/WPFClient;component/Images/away.png" />
<BitmapImage x:Key="UnknownIcon" UriSource="/WPFClient;component/Images/unknown.png" />
My List :
<ListBox ItemsSource="{Binding Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Image Source="{Binding Status, Converter={StaticResource UserStatusToIconConverter}}" Height="16" Width="16" Margin="0,0,5,0" />
<TextBlock Text="{Binding Nick}" />
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My converter :
public class UserStatusToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string userStatus = value.ToString();
string iconName = "UnknownIcon";
switch (userStatus)
{
case "Active":
iconName = "ActiveIcon";
break;
case "Idle":
iconName = "IdleIcon";
break;
case "Away":
iconName = "AwayIcon";
break;
}
return iconName;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
NOTE: The code also DOES NOT work as it currently stands, no image is actually displayed. However I assume thats a minor detail somewhere.
Try using VirtualizationStackPanel will give some improvements
<ListBox VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
</ListBox>
in framework 4.5 VirtualizingPanel is available and setting VirtualizingPanel.ScrollUnit="Item" will give good performance improvement
The easiest way to solve that is to change converter to:
public class UserStatusToIconConverter : IValueConverter
{
private static readonly Uri ActiveIcon = new Uri("pack://application:,,,/WPFClient;component/Images/active.png");
private static readonly Uri IdleIcon = new Uri("pack://application:,,,/WPFClient;component/Images/idle.png");
private static readonly Uri AwayIcon = new Uri("pack://application:,,,/WPFClient;component/Images/away.png");
private static readonly Uri UnknownIcon = new Uri("pack://application:,,,/WPFClient;component/Images/unknown.png");
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var userStatus = value.ToString();
switch (userStatus)
{
case "Active":
return ActiveIcon;
case "Idle":
return IdleIcon;
case "Away":
return AwayIcon;
default:
return UnknownIcon;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The slow-down is caused by improper binding.

textbox visibility/collapsed in silverlight mvvm

I Silverlight5 with mvvm pattern i have one doubt.
In xaml i have used one textblock and i bind some id in it.
If the textblock content value is 1 or 2 means
yet another textbox is visible or else that is collapsed.. how to acheive that..
my code:
<TextBlock Name="textBlock1" Text="{Binding id}" Loaded="textBlock1_Loaded" Visibility="Collapsed" />
<TextBox Text="{Binding name,Mode=TwoWay}" x:Name="t1" Visibility="{Binding IsVisible,Converter={StaticResource visibilityconverter}}" />
in view model i had created the property for id and raised the event and bind the value to textblock.
to convert the value to visible i have a visibilityconverter class in one separate folder named "Converters"
public class visibilityconverter:IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (id==1 && id==2)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
in the above visibleconverter class how i cna get the id value from viewmodel and check it..
If i got the value from viewmodel to visibilityconverter means i will proceed further.
tell me if u can..!
Hi i have found the solution..
In xaml give the following:
<TextBox Text="{Binding name,Mode=TwoWay}" x:Name="t1" Visibility="{Binding id,Converter={StaticResource visibilityconverter}}" />
In visibilityConverter class:
public class visibilityconverter:IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
string v = value.ToString();
if (v =="1" || v=="2")
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Problem Solved... !

Automatically capitalize first letter in TextBoxes

I am creating a wpf application. I have to make all textbox first letter to capital, if a user entered in small then it should be formatted in capital on mouse out.I need the best way to do it, please someone help me.
The best way of doing it greatly depends on how you are doing your app, but #H.B.'s answer is probably the way to go.
For the sake of completeness, another way if doing it would be to use a converter like so:
<!-- Your_Window.xaml -->
<Window x:Class="..."
...
xmlns:cnv="clr-namespace:YourApp.Converters">
<Window.Resources>
<cnv.CapitalizeFirstLetterConverter x:Key="capFirst" />
</Window.Resources>
...
<TextBox Text="{Binding Path=SomeProperty, Converter={StaticResource capFirst}}" />
This assumes that your window's data context is set to an instance of a class that has a read/write property named SomeProperty of type string.
The converter itself would be something like this:
// CapitalizeFirstLetterConverter.cs
using System;
using System.Data;
using System.Globalization;
namespace YourApp.Converters {
[ValueConversion(typeof(string), typeof(string))]
public class CapitalizeFirstLetterConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
// this will be called after getting the value from your backing property
// and before displaying it in the textbox, so we just pass it as-is
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
// this will be called after the textbox loses focus (in this case) and
// before its value is passed to the property setter, so we make our
// change here
if (value is string) {
var castValue = (string)value;
return char.ToUpper(castValue[0]) + castValue.Substring(1);
}
else {
return value;
}
}
}
}
You can learn more about converters here.
You could put a style into the Application.Resources to handle LostFocus on all TextBoxes, then you just need to change the Text property accordingly.
<!-- App.xaml - Application.Resources -->
<Style TargetType="{x:Type TextBox}">
<EventSetter Event="LostFocus" Handler="TextBox_LostFocus" />
</Style>
// App.xaml.cs - App
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var tb = (TextBox)sender;
if (tb.Text.Length > 0)
{
tb.Text = Char.ToUpper(tb.Text[0]) + tb.Text.Substring(1);
}
}
I'm a bit late to the game, but if anybody else needs it this dll capitalizes the first letter in realtime. For example, you don't need to mouse out.
http://www.mardymonkey.co.uk/blog/auto-capitalise-a-text-control-in-wpf/
Perhaps you can use a converter but not a converter like #ssarabando, because it is bugged.
Here's the code of the converter:
using System;
using System.Globalization;
using System.Windows.Data;
namespace SistemaContable.GUI.WPF.Converters
{
public class CapitalizeFirstLetter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
string stringToTitleCase = culture.TextInfo.ToTitleCase(value.ToString());
return stringToTitleCase;
}
else
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString();
}
}
}
You need to reference it in a "ResourceDictionary" or in your "App.xaml":
<ResourceDictionary xmlns:converters="clr-namespace:SistemaContable.GUI.WPF.Converters">
<converters:CapitalizeFirstLetter x:Key="CapitalizeFirstLetter"/>
</ResourceDictionary>
And you can use it like this:
<TextBox x:Name="txtNombre" Text="{Binding Usuario.Nombre, Converter={StaticResource CapitalizeFirstLetter}, UpdateSourceTrigger=PropertyChanged}"/>

wpf data binding grid row visibility

I have a window with a grid acting like a form. The window isn't my own and there is a new requirement to not show (ie, collapse) rows 4 and 5 based on a user selected context.
The two things I can think of to make this work is to either:
Have a converter on the row content which takes a bool and collapses the visibility if true.
Have a converter on the grid row height property.
I prefer the latter, but am at a loss to get the input value for the converter. The converter code and binding is below.
Can someone tell me what the binding should look like to make this work? Is there some easier way to do this?
Converter Code
[ValueConversion(typeof(GridLength), typeof(Visibility))]
public class GridLengthToCollapseVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || parameter == null) return Binding.DoNothing;
var result = (GridLength) value;
bool shouldCollapse;
Boolean.TryParse(parameter.ToString(), out shouldCollapse);
return shouldCollapse ? new GridLength() : result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
}
The Binding (this is where I am stuck)
Say I want the value of the height to be 30 unless the bound ShowLastName property is true.
The binding below isn't right, but what is?
<RowDefinition Height="{Binding Source=30, Converter={StaticResource GridLengthToCollapseVisibilityConv},ConverterParameter=ShowLastName}" />
Working Solution
[ValueConversion(typeof(bool), typeof(GridLength))]
public class GridLengthToCollapseVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || parameter == null) return Binding.DoNothing;
bool shouldCollapse;
Boolean.TryParse(value.ToString(), out shouldCollapse);
return shouldCollapse
? new GridLength(0)
: (GridLength) parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
}
<Grid.Resources>
<cvt:GridLengthToCollapseVisibilityConverter x:Key="GridLengthToCollapseVisibilityConv" />
<GridLength x:Key="AutoSize">Auto</GridLength>
<GridLength x:Key="ErrorLineSize">30</GridLength>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="{StaticResource AutoSize}" />
<RowDefinition Height="{StaticResource ErrorLineSize}" />
<RowDefinition Height="{Binding Path=HideLastName,
Converter={StaticResource GridLengthToCollapseVisibilityConv},ConverterParameter={StaticResource AutoSize}}" />
<RowDefinition Height="{Binding Path=HideLastName,
Converter={StaticResource GridLengthToCollapseVisibilityConv},ConverterParameter={StaticResource ErrorLineSize}}" />
</Grid.RowDefinitions>
You can't databind the ConverterParamater: http://social.msdn.microsoft.com/Forums/en/wpf/thread/88a22766-5e6f-4a16-98a6-1ab39877dd09
Why not switch the value and parameter if the height always is the same:
<RowDefinition Height="{Binding Source=ShowLastName, Converter={StaticResource GridLengthToCollapseVisibilityConv},ConverterParameter=30}" />
If you need to databind both values you could use multi-value bindings: http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.aspx
All you have to do is to swap Binding and Parameter.
If you still want both values to be databound, use MultiBinding, even if your second value is a constant. It's a hack, but it's the easiest way to pass extra value(s) into your converter.

Binding in WPF to element of array specified by property

Say I've got some TextBlocks on my UI, something like so:
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding DessertIndex}" />
<TextBlock Text="{Binding Food[2]}" />
<TextBlock Text="{Binding Food[{Binding DessertIndex}]}" />
</StackPanel>
and in my code behind I've got something like this:
public partial class MainWindow : Window
{
public int DessertIndex
{
get { return 2; }
}
public object[] Food
{
get
{
return new object[]{"liver", "spam", "cake", "garlic" };
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
The first two TextBlocks display fine for me, displaying 2 and 'cake' respectively. The third one doesn't accomplish what I'd like, namely use the DessertIndex property to index into that array and also display 'cake'. I did a little searching here on SO for a similar question but didn't find one. Ultimately, I don't want to specify values like 2 in my .xaml file and would like to rely upon a property instead for indexing into that array. Is this possible? If so, what am I doing wrong here?
EDIT:
So what I more closely have is a situation where the data is a List of these object[] and I'm using the above StackPanel as part of a DataTemplate for a ListBox. So the idea, as Mark Heath suggests below, of using a property that dereferences the array doesn't seem to work as I'd want. Ideas?
Another alternative is to use MultiBinding with a converter:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<local:FoodIndexConverter x:Key="foodIndexConverter" />
</StackPanel.Resources>
<TextBlock Text="{Binding DessertIndex}" />
<TextBlock Text="{Binding Food[2]}" />
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource foodIndexConverter}">
<Binding Path="DessertIndex" />
<Binding Path="Food"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
Then in the code-behind, the converter is defined something like this:
namespace WpfApplication1
{
public class FoodIndexConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values == null || values.Length != 2)
return null;
int? idx = values[0] as int?;
object[] food = values[1] as object[];
if (!idx.HasValue || food == null)
return null;
return food[idx.Value];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
if you are going to the trouble of having a DesertIndex property on your DataContext, why not a property that dereferences the Food array with DesertIndex:
public object SelectedFood
{
get { return Food[DessertIndex]; }
}
public int DessertIndex
{
get { return 2; }
}
public object[] Food
{
get
{
return new object[]{"liver", "spam", "cake", "garlic" };
}
}
then you can bind directly to that:
<TextBlock Text="{Binding SelectedFood}" />
This is essentially the "MVVM" approach: make the datacontext object have properties that are just right for binding to.
Just To add on the great answer by Colin Thomsen.
You could also use C# dynamic keyword to make this solution work with pretty much every container type. Or even bind to multidimensional containers "{Binding Food[{Binding DessertIndex1}][{Binding DessertIndex2}]}"
public class ContainerDoubleAccessConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
dynamic idx1 = values[0];
dynamic idx2 = values[1];
dynamic container = values[2];
return container[idx1][idx2];
}
catch (System.Exception err)
{
DebugTrace.Trace("bad conversion " + err.Message);
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}

Resources