Dynamic image source binding in silverlight - c#

I want to set an image's source according to its DataContext in a ChildWindow. Here is the XAML file:
<controls:ChildWindow x:Class="CEM.Controls.DialogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" Title="{Binding Title}">
...
<Image x:Name="DialogIcon"></Image>
...
</controls:ChildWindow>
It's working fine if I override the Show method of the ChildWindow and set the image's source:
public new void Show()
{
DialogIcon.Source = new BitmapImage(new Uri(#"/Images/DialogWindow/Confirm.png", UriKind.Relative));
base.Show();
}
But it looks ugly and it's not the "silverlight way", so I decide to change:
<Image x:Name="DialogIcon" Source="{Binding DialogIconType, Converter={StaticResource DialogIconConverter}}"></Image>
You see I have a DialogIconConverter registered to bind the source from the DataContext.
public class DialogIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//currently it's an hard-coded path
return new BitmapImage(new Uri(#"/Images/DialogWindow/Confirm.png", UriKind.Relative));
}
...
}
But it's not working now, I have several other converters in this control which are working fine. Only this one is not working. Can you help to find where the problem is?
EDIT: DialogIconType is an enum, and also it's a property of DialogContext. An instance of DialogContext will be assigned to DataContext property of the DialogWindow.
public enum DialogIconType
{
Confirm,
Alert,
Error
}
public class DialogContext
{
public string Title { get; set; }
public string Content { get; set; }
public DialogButtons Buttons { get; set; }
public DialogIconType IconType { get; set; }
}
internal DialogWindow(DialogContext context)
{
InitializeComponent();
this.DataContext = context;
}

might be silly, but did you make sure that your converter is referenced properly in your xaml file ?
otherwise, I suggest trying this syntax as path for your URI (with images setup as resources):
return new BitmapImage(new Uri("pack://application:,,,/Images/DialogWindow/Confirm.png", UriKind.Relative));
EDIT :
ok, I think I've got it :
look into your output window, you will probably see some error 40 binding ... blablabla...
My guess is that the converter is right, but the source of the binding isn't, so basically the converter is not even used.
The reason is that your DialogIconType is not a dependency property, so it cannot be bound to.
in other words, this :
public DialogIconType IconType { get; set; }
should become this :
public static DependencyProperty IconTypeProperty = DependencyProperty.Register("IconType", typeof(DialogIconType), typeof(DialogContext));
public DialogIconType IconType
{
get { return (DialogIconType)(GetValue(IconTypeProperty)); }
set { SetValue(IconTypeProperty , value); }
}
plus, in your Xaml, you should Bind to "IconType", and not "DialogIconType" (which is a type and not a property)
(this might even be the sole issue, as I'm not sure if a dependencyProperty Is actually realy needed here, now that I think of it)

Assuming that DialogIconType is the path to your image (e.g. "Images/DialogWindow/Confirm.png"), it should work without a valueconverter as shown below:
<Image Source="{Binding DialogIconType}" />
EDIT:
Returning the path to the image from the valueconverter's Convert method is also possible - i.e.:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "Images/DialogWindow/Confirm.png";
}
EDIT 2:
The following also works using UriKind.Relative:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new BitmapImage(new Uri("Images/DialogWindow/Confirm.png", UriKind.Relative));
}

Related

WPF Two Way Data Binding to Textbox.Text only working in one direction

My question is not about serialization I am getting the serialization part to work just fine, but for context I am trying to serialize some properties set by the user in a WPF form, and therefore require a two-way binding (which I can only get to work in one direction: target-to-source updates).
Let's say I have defined an application property that is a container for my properties I want to be serializable:
public SerializableApplicationProperties ThisAppsSerializableProperties { get; set; }
The SerializableApplicationProperties class looks like this:
[Serializable]
public class SerializableApplicationProperties
{
public SerializableApplicationProperties()
{
UserSelectedPreFillFilePath = new ObservableString();
}
public SerializableApplicationProperties(string defaultFilePath)
{
UserSelectedPreFillFilePath = new ObservableString(defaultFilePath);
}
public ObservableString UserSelectedPreFillFilePath { get; set; }
}
I have one property defined so far that I need to establish a two way-binding for in code (yes, in code). Please do not suggest how I can do this in XAML markup, unless you also explain how to do it entirely in code.
The property is "UserSelectedPreFillFilePath", which you can see is of type "ObservableString".
ObservableString Class looks like this:
[Serializable]
public class ObservableString : INotifyPropertyChanged
{
private string _stringValue;
public string StringValue
{
get { return _stringValue; }
set
{
_stringValue = value;
OnPropertyChanged("StringValue");
}
}
public ObservableString() { }
public ObservableString(string value)
{
this._stringValue = value;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
A very simple class that holds a "StringValue" property and implements INotifyChange.
So then, I try to establish a two-way binding between this "UserSelectedPreFillFileLocation" and a text box on the main window.
I want a two-way binding so that before I close down the application I can serialize the UserSelectedPreFillFileLocation property (which should be automatically updated based on what the user puts in the textbox in the window).
Then when the application loads from a file (deserializes) that textbox is auto-populated with the UserSelectedPreFillFileLocation value via the binding.
Second to last thing, my converter class definition:
[ValueConversion(typeof(ObservableString), typeof(string))]
class ObservableStringToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ObservableString myVar = (ObservableString)value;
return myVar.StringValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string myVar = (string)value;
return new ObservableString(myVar);
}
}
Lastly here is the definition of the binding. The binding works target-to-source but not the other way around. I.E. when the user updates the textbox, UserSelectedPreFillFileLocation is updated, but not the other way around.
Binding UserSelectedPreFillFilePathBinding = new Binding();
UserSelectedPreFillFilePathBinding.Path = new PropertyPath(nameof(ThisAppsSerializableProperties.UserSelectedPreFillFilePath));
UserSelectedPreFillFilePathBinding.Source = ThisAppsSerializableProperties;
UserSelectedPreFillFilePathBinding.Converter = new ObservableStringToStringConverter();
UserSelectedPreFillFilePathBinding.Mode = BindingMode.TwoWay;
UserSelectedPreFillFilePathBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ThisAppInstanceMainWindow.TxtbxPrefillFileLocation.SetBinding(TextBox.TextProperty, UserSelectedPreFillFilePathBinding);
I discovered what I was doing wrong.
I have indeed defined an ObservableString and the observable property that raises the property changed event is the StringValue within the class.
So, in this case I did not even need a converter since the source property I should have defined for the binding was the StringValue, and the source object should have been the UserSelectedPreFillFilePath.
Corrected code that is now working great with the two-way binding:
Binding UserSelectedPreFillFilePathBinding = new Binding();
UserSelectedPreFillFilePathBinding.Path = new PropertyPath(nameof(ThisAppsSerializableProperties.UserSelectedPreFillFilePath.StringValue));
UserSelectedPreFillFilePathBinding.Source = ThisAppsSerializableProperties.UserSelectedPreFillFilePath;
UserSelectedPreFillFilePathBinding.Mode = BindingMode.TwoWay;
UserSelectedPreFillFilePathBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ThisAppInstanceMainWindow.TxtbxPrefillFileLocation.SetBinding(TextBox.TextProperty, UserSelectedPreFillFilePathBinding);

Write simple converter

I have this Class:
public class MyData
{
public static int Total Files;
public static int Total FilesFinished;
}
And I have simple Progress-Bar that calculate its Value this way:
double value = ((double)MyData.FilesFinished / MyData.Files) * 100;
And update my Label using simple Timer:
Label name="lblPercentage" />
lblPercentage.Content = value;
Now I want to use Converter instead of updating my Label via code behind.
So I have this class (not implemented yet):
public class TotalFilesToTotalPercentageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Inside my Window.Resource I have this:
<Convertors:TotalFilesToTotalPercentageConverter x:Key="FilesToPercentageConverter "/>
And this is what I have try inside my Label:
Content="{Binding Converter={StaticResource FilesToPercentageConverter}}"
So my problem is that I try to see if my TotalFilesToTotalPercentageConverter class is responding via the debugger and it seems not, nothing happening.
What did I do wrong?
Update
I forget to mention that my TotalFilesToTotalPercentageConverter class in inside Converter folder under Utils folder under Classes folder
You need to bind the Content property to a source property for your Convert method to be invoked. Converters only work with data bindings.
This means that instead of setting the Content property of the Label in the code-behind like this:
lblPercentage.Content = value;
You should set a source property of a view model that you then bind the Content property of the Label to:
Content="{Binding Path=YourValueProperty, Converter={StaticResource FilesToPercentageConverter}}"
Set the DataContext of your view to an instance of your view model class:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
The view model class needs to implement the INotifyPropertyChanged and raise the PropertyChanged event in the setter of the source property (YourValueProperty).

String.Format Is it possible to display file name for Path string in XAML [duplicate]

This question already has an answer here:
File path to file name String converter not working
(1 answer)
Closed 6 years ago.
Hi i am new to C# and WPF. The below is just example not tested.
Lets say i have
String path1 = "C:\Users\user\Desktop\File.ext"
String path2 = "C:\temp\text.txt"
String.Format(path1.Substring(path1.LastIndexOf('\')+1))
String.Format(path2.Substring(path1.LastIndexOf('\')+1))
I want to get the filename from the path without changing the original string and i want to do it preferably in XAML or do a value converter.
I am looking into this to know if I should do a value converter between one Observable collection and ListView or just stick with two Lists. One to contain filepaths in model manually and fileNames to display in a ListView and update both separately (Current approach).
My data model
private List<String> AttachedFiles;
public ObservableCollection<String> fileNames { get; set; }
public void addFilePath(String filePath) {
this.AttachedFiles.Add(filePath);
}
public void removeFilePath(String filePath)
{
this.AttachedFiles.Remove(filePath);
}
The xaml
<ListView x:Name="DropList" ItemsSource="{Binding fileNames}"
I included very little code as this as to give an just an idea as I am thinking of refactoring. Can i use String methods inside String.Format for dipsplaying in XAML so it will still have the original filepath value in the model or is there some syntax for String.Format to do this?
EDIT
The answers are duplicated but I was asking if String.Format can be used in the XAML converter with methods like Trim Substring Etc and if I should refactor my code, but doing it properly creates more code and complexity than it removes.
EDIT 2
This is the code. Basically the list view has an X icon and text with file name next it, if the user clicks the icon the it will call this:
private void r1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Rectangle r = sender as Rectangle;
ticket.removeFilePath(r.DataContext.ToString());
ticket.fileNames.Remove(r.DataContext.ToString());
}
Use one collection, and don't use List<T>:
ObservableCollection<String> FilePaths { get; private set; }
If you replace the collection, you'll need to raise INotifyPropertyChanged.PropertyChanged.
Write a value converter (OK, just steal mine):
public class FileName : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.IO.Path.GetFileName(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And use the value converter in a DataTemplate. Since FilePaths is a collection of String, the DataContext of your DataTemplate will be a String, so the binding won't specify a property for the binding to use. The value passed to the value converter will be the string itself.
<ListView x:Name="DropList" ItemsSource="{Binding FilePaths}">
<ListView.ItemTemplate>
<DataTemplate>
<Label
Content="{Binding Converter={local:FileName}}"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you are dealing with only one property here, you can just use a converter as other answer suggested. But you could also create a new Class for your Path object, and define a FilePath string property in this class to store the full path. (And more properties if you need.)
Then you can just override the ToString method in this Class to return just the filename.
By doing this, you don't need to change the XAML, but the binding list will just display the item as the result returned by your override ToString method.
Something like:
public ObservableCollection<MyPath> fileNames { get; set; }
public class MyPath: ObservableObject //implement INotifyPropertyChanged
{
private string _filepath;
public string FilePath
{
get { return _filepath; }
set
{
if (value != _filepath)
{
_filepath= value;
RaisePropertyChanged("FilePath");
}
}
}
public override string ToString()
{
return System.IO.Path.GetFileName(FilePath);
}
}

Fill ComboBox with images from Resources.resx [duplicate]

I have a WPF application that needs to provide feedback to the user about an internal state. The design is to have three images, call them Red, Yellow, and Green. One of these images will be displayed at a time depending on the state. Here are the points:
The three images are in Properties.Resources in the code-behind
Only one of the images will be shown at a time.
The state change comes from a process in code-behind and not from the user.
I would like to bind an image control so that I can change the image from code-behind.
I’m assuming I’ll need an image converter to change the JPG image to an image source such as:
[ValueConversion(typeof(System.Drawing.Bitmap), typeof(ImageSource))]
public class BitmapToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var bmp = value as System.Drawing.Bitmap;
if (bmp == null)
return null;
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I’d prefer to convert the images once during initialization and keep a list of Image sources. I’m also assuming I’ll need a dependency property to bind the control to, but I’m not sure how to set that up with this list of image sources:
// Dependancy Property for the North Image
public static readonly DependencyProperty NorthImagePathProperty
= DependencyProperty.Register(
"NorthImagePath",
typeof(ImageSource),
typeof(MainWindow),
new PropertyMetadata("**Don't know what goes here!!!**"));
// Property wrapper for the dependancy property
public ImageSource NorthImagePath
{
get { return (ImageSource)GetValue(NorthImagePathProperty); }
set { SetValue(NorthImagePathProperty, value); }
}
Although an image resource in a WPF project generates a System.Drawing.Bitmap property in Resources.Designer.cs, you could directly create a BitmapImage from that resource. You only need to set the Build Action of the image file to Resource (instead of the default None).
If you have a file Red.jpg in the Resources folder of your Visual Studio Project, creating a BitmapImage would look like shown below. It uses a WPF Pack Uri.
var uri = new Uri("pack://application:,,,/Resources/Red.jpg");
var bitmap = new BitmapImage(uri);
If you have an Image control declared somewhere in XAML like this:
<Image x:Name="image"/>
you could simply set the Source property of the image to your BitmapImage in code behind:
image.Source = bitmap;
In case you prefer to set the Source property by binding you could create a string property that returns the image URI. The string will automatically be converted to a BitmapImage by a built-in TypeConverter in WPF.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
ImageUri = "pack://application:,,,/Resources/Red.jpg";
}
public static readonly DependencyProperty ImageUriProperty =
DependencyProperty.Register("ImageUri", typeof(string), typeof(MainWindow));
public string ImageUri
{
get { return (string)GetValue(ImageUriProperty); }
set { SetValue(ImageUriProperty, value); }
}
}
In XAML you would bind to that property like this:
<Image Source="{Binding ImageUri}"/>
Of course you could as well declare the property to be of type ImageSource
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(MainWindow));
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
and bind in the same way:
<Image Source="{Binding Image}"/>
Now you could pre-load your images and put them into the property as needed:
private ImageSource imageRed =
new BitmapImage(new Uri("pack://application:,,,/Resources/Red.jpg"));
private ImageSource imageBlue =
new BitmapImage(new Uri("pack://application:,,,/Resources/Blue.jpg"));
...
Image = imageBlue;
UPDATE: After all, your images do not need to be resources in the Visual Studio project. You could just add a project folder, put the image files into that folder and set their Build Action to Resource. If for example you call the folder Images, the URI would be pack://application:,,,/Images/Red.jpg.

Changing CardView background colour through MvvmCross view model

I'm trying to bind the background colour of my CardView to my view model, but I'm getting this error back from Mvx:
MvxBind:Warning: 11.66 Failed to create target binding for binding
CardBackgroundColor
I'm not sure whether I'm using the wrong property binding in the AXML or in the view model.
Here is the property I'm trying to bind it to in the view model:
public int EventEntryBackgroundColour
{
get
{
return IsRead
? Resource.Color.yellow
: Resource.Color.White;
}
}
I've also tried using it as a string type to return a colour in HEX, but it still doesn't work.
Here's the attribute, I'm setting on the CardView
cardview:MvxBind="CardBackgroundColor EventEntryBackgroundColour"
Any help with this would be much appreciated.
I faced the same issue. My solution...
Converter:
public class MessageStatusToColorDrawableConverter : MvxValueConverter<bool, ColorDrawable>
{
protected override ColorDrawable Convert(bool value, Type targetType, object parameter, CultureInfo cultureInfo)
{
var context = Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity; // To get the context of the activity
return value ? new ColorDrawable(new Color(ContextCompat.GetColor(context, Resource.Color.Pink))) : new ColorDrawable(new Color(ContextCompat.GetColor(context, Resource.Color.Green)));
}
}
XML:
<RelativeLayout
android:id="#+id/relay_archive"
android:layout_width="10dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:paddingLeft="10dp"
android:paddingRight="10dp"
local:MvxBind="Background MessageStatusToColorDrawable(Status)">
We can't bind an Android.Graphics.Color property to local:MvxBind="BackgroundColor... since the BackgroundColor property wants you to use the NativeColor converter which uses an MvxColor.
However, the local:MvxBind="Background... property wants a drawable, hence my use of binding a ColorDrawable.
You need to look into the Value converters of MvvmCross: https://github.com/MvvmCross/MvvmCross/wiki/Value-Converters#the-mvx-color-valueconverters
Using those you can set a color like this:
local:MvxBind="BackgroundColor NativeColor(CurrentColor)"
Or for iOS:
set.Bind(field)
.For(field => field.BackgroundColor)
.To(vm => vm.CurrentColor)
.WithConversion("NativeColor");
And Windows:
Fill="{Binding CurrentColor, Converter={StaticResource NativeColor}}"
I just defined a subclass of my cardview with a own property for it, since you cant directly access it else it seems and i didnt find a binding for it yet?
public class BindableColorCardView: CardView
{
private Color m_cCardViewColor;
public Color CardViewColor
{
get { return m_cCardViewColor; }
set
{
m_cCardViewColor = value;
SetCardBackgroundColor(m_cCardViewColor);
}
}
And then just used binded property with a ValueConverter.
I know this was asked a very long time ago but maybe it will help somebody out.
The answer now is that you need to use CardViewBackgroundColor instead of CardBackgroundColor.
So in your case that would be:
cardview:MvxBind="CardViewBackgroundColor EventEntryBackgroundColour"
You should bind it to a Android.Graphics.Color though and not an integer.
To do that, you can create a converter class like below:
public class BackgroundColorConverter : MvxValueConverter<bool, Color>
{
protected override Color Convert(bool value, Type targetType, object parameter, CultureInfo culture)
{
return value ? Color.Black : Color.White;
}
}
You can call this converter from the xaml too and pass a boolean from your ViewModel to display the correct color. Example:
cardview:MvxBind="CardViewBackgroundColor BackgroundColor(Selected)"

Categories

Resources