UWP Show Image Dynamically Based on a Value of a Bindable Property - binding

I am trying to learn how bindings and converters work and I am struggling
How do I bind an image to a ViewCell based on a binable property called Status?
For example:
if my Status = "X", show Image1
if my Status = "Y", show Image2
if my Status = "Z", show Image3
I have an Image which Source is set to the Status property like below but this is not working, image is not displayed. My guess is that I need to write a converter to somehow convert the Status value to an image
<Image x:Name="StatusIcon" Source="{Binding Status}"/>
My images are locaed in the UWP project root and if I do this:
<Image x:Name="StatusIcon" Source="ms-appx:///Image1.png" />
, then my image will show properly but I need it to change dynamically based on the Status property.

You are right. You need to use a converter to convert the string to corresponding Images.
Here is a sample code
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
switch (value as string)
{
case "X":
return new BitmapImage(new Uri("ms-appx:///Image1.png"));
case "Y":
return new BitmapImage(new Uri("ms-appx:///Image2.png"));
case "Z":
return new BitmapImage(new Uri("ms-appx:///Image3.png"));
default:
return new BitmapImage(new Uri("ms-appx:///Default.png"));
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}

Related

Convert a path (which is an image) to HttpPostedFileBase

I provide for the User to upload an image.
But if it is not done, I want to set a default image in an images folder.
How do I set the model property of type 'HttpPostedFileBase' to the image that is located at the path that I set programmatically?
The model property:
public HttpPostedFileBase UploadedImage { get; set; }
I have tries these with no success.
This one I get: Cannot convert type 'string' to 'System.Web.HttpPostedFileBase'.
if (userProfileForSaveVM.UserProfileSingleVM.UploadedImage == null)
{
// Set a default.
userProfileForSaveVM.UserProfileSingleVM.UploadedImage =
"F:\\GbngWebClient\\GbngWebClient\\Images\\Avatar.png";
}
This one I get: property or indexer 'HttpPostedFileBaseInputStream' cannot be assigned to -- it is read only.
if (userProfileForSaveVM.UserProfileSingleVM.UploadedImage == null)
{
// Set a default.
String pathImage = Server.MapPath("F:\\GbngWebClient\\GbngWebClient\\Images\\Avatar.png");
userProfileForSaveVM.UserProfileSingleVM.UploadedImage.InputStream = new FileStream(pathImage, FileMode.Open);
}
This one I get: Cannot convert type 'byte[]' to 'System.Web.HttpPostedFileBase'.
if (userProfileForSaveVM.UserProfileSingleVM.UploadedImage == null)
{
// Set a default.
byte[] imageArray =
System.IO.File.ReadAllBytes(#F:\\GbngWebClient\\GbngWebClient\\Images\\Avatar.png");
userProfileForSaveVM.UserProfileSingleVM.UploadedImage = imageArray;
}
I am not sure why you would want to create a class of this type. The class is for receiving file data from a HTTP Request. Usually you get data from this class using its input stream. The msdn docs have an example here:
https://learn.microsoft.com/en-us/dotnet/api/system.web.httppostedfile.inputstream?view=netframework-4.8#System_Web_HttpPostedFile_InputStream
What you want to do is get the File Stream from the HttpPostedFileClass, and if there is none, get that file stream from your default location. You can declare an empty memory stream to start with and fill that stream from either the HttpPostedFile or your local resource where appropriate.
try use check null in View like:
if (userProfileForSaveVM.UserProfileSingleVM.UploadedImage != null)
{
<img src= "#Url.Content("~/uploads/FileUpload12011_03_02_11_49_22.jpg")" alt="IMAGES" />
}

Neo4j - Custom converter for field of type List

I am trying to write a custom converter for a nested object so that this object gets saved as string in Neo4j database.
I am using #Convert annotation on my field and passing ImageConverter.class which is my AttributeConverter class.
Everything works fine as expected and I am able to save string representation of Image class in Neo4j db.
However, now instead of single image I want to have List<Image> as my nested field. In this case, putting #Convert(ImageConverter.class) doesn't work.
I see that there is a class called ConverterBasedCollectionConverter which gets used when I have a field of type List<LocalDateTime.
However, I couldn't find any exammples on how to use this class in case of custom converters.
Please can anyone help me with this or if there is any other approach to use custom converter on field of type List.
I am using Neo4j (version 3.4.1) and Spring-data-neo4j (5.0.10.RELEASE) in my application. I am also using OGM.
PS: I am aware that it is advised to store nested objects as separate node establishing a relationship with parent object. However, my use case demands that the object be stored as string property and not as separate node.
Regards,
V
It is not so difficult as I assumed it would be.
Given a class (snippet)
#NodeEntity
public class Actor {
#Id #GeneratedValue
private Long id;
#Convert(MyImageListConverter.class)
public List<MyImage> images = new ArrayList<>();
// ....
}
with MyImage as simple as can be
public class MyImage {
public String blob;
public MyImage(String blob) {
this.blob = blob;
}
public static MyImage of(String value) {
return new MyImage(value);
}
}
and a converter
public class MyImageListConverter implements AttributeConverter<List<MyImage>, String[]> {
#Override
public String[] toGraphProperty(List<MyImage> value) {
if (value == null) {
return null;
}
String[] values = new String[(value.size())];
int i = 0;
for (MyImage image : value) {
values[i++] = image.blob;
}
return values;
}
#Override
public List<MyImage> toEntityAttribute(String[] values) {
List<MyImage> images = new ArrayList<>(values.length);
for (String value : values) {
images.add(MyImage.of(value));
}
return images;
}
}
will print following debug output on save that I think is what you want:
UNWIND {rows} as row CREATE (n:Actor) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-1, props={images=[blobb], name=Jeff}}]}
especially the images part.
Test method for this looks like
#Test
public void test() {
Actor jeff = new Actor("Jeff");
String blobValue = "blobb";
jeff.images.add(new MyImage(blobValue));
session.save(jeff);
session.clear();
Actor loadedActor = session.load(Actor.class, jeff.getId());
assertThat(loadedActor.images.get(0).blob).isEqualTo(blobValue);
}
I am came up with a solution to my problem. So, in case you want another solution along with the solution provided by #meistermeier, you can use the below code.
public class ListImageConverter extends ConverterBasedCollectionConverter<Image, String>{
public ListImageConverter() {
super(List.class, new ImageConverter());
}
#Override
public String[] toGraphProperty(Collection<Image> values) {
Object[] graphProperties = super.toGraphProperty(values);
String[] stringArray = Arrays.stream(graphProperties).toArray(String[]::new);
return stringArray;
}
#Override
public Collection<Image> toEntityAttribute(String[] values) {
return super.toEntityAttribute(values);
}
}
ImageConverter class just implements AttributeConverter<Image, String> where I serialize and deserialize my Image object to/from json.
I chose to go with this approach because I had Image field in one object and List<Image> in another object. So just by changing #Convert(ListImageConverter.class) to #Convert(ImageConverter.class) I was able to save list as well as single object in Neo4j database.
Note: You can skip overriding toEntityAttribute method if you want. It doesn't add much value.
However you have to override toGraphProperty as within Neo4j code it checks for presence of declared method with name toGraphProperty.
Hope this helps someone!
Regards,
V

Image in TableView (Subtitle) MVVM

This may seem like a silly question but I am having a hard time finding the correct answer. I am writing an application using MVVMCross and Xamarin for iOS. In the application i want to use a TableView using the standard Subtitle Cell style. I am able to successfully bind to the title and detail text, but have so far been unsuccessful in binding the Image to a local file. Most examples I see are using images pulled from the web, whereas here i would like to use an image from the Resources folder. I am also trying to avoid having to write a custom cell.
How would you bind a local image to the standard Subtitle Cell style using MVVMCross and Xamarin?
Line of code in question:
var source = new MvxStandardTableViewSource (TableView, UITableViewCellStyle.Subtitle, new NSString( "CellID" ), "ImageURL Image; TitleText Title; DetailText EventDate");
Thanks!
You can do this using a converter:
public class ItemTypeToImageValueConverter : IMvxValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string assetName = "unknown";
switch ((ItemType)value) {
case ItemType.SOMETHING:
assetName = "something";
break;
default:
throw new ArgumentOutOfRangeException ();
}
return "res:item_" + assetName + ".png";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then to bind it to your UI:
private readonly MvxImageViewLoader typeImageViewLoader;
public ItemItemTableViewCell(IntPtr handle)
: base(handle)
{
typeImageViewLoader = new MvxImageViewLoader(() => BackgroundImageView);
this.DelayBind(() =>
{
var set = this.CreateBindingSet<ItemItemTableViewCell, Item>();
set.Bind(typeImageViewLoader).To(vm => vm.Type).WithConversion("ItemTypeToImage");
set.Apply();
});
}

Text on TextBox with UpdateSourceTrigger=PropertyChanged is not updated when coercion of text input results in unchanged source value

I have a text box whose Text property has a TwoWay MultiBinding with UpdateSourceTrigger set to PropertyChanged. The first Binding is to a dependency property (Value) which has a PropertyChangedCallBack function that rounds the value to one decimal place.
The purpose of the text box is to perform the rounding as the user types rather than when the text box loses focus, hence why UpdateSourceTrigger is set to PropertyChanged.
The problem I am having is that if text is entered that does NOT result in Value changing, the Text property and Value become out of sync. Only if the rounding operation causes Value to change does Text get updated on the fly. E.g., if Text and Value are both 123.4 and the user types 1 after this, Value is rounded to the same value (123.4), but Text shows 123.41. However, if 9 is then typed after the 4, Value is rounded up to 123.5. And because of this actual change, Text is then updated to the same (123.5).
Is there any way of forcing a text box to update from its source even when the source hasn't changed since the last trigger? I have tried using BindingExpressionBase.UpdateTarget() but this only works when UpdateSourceTrigger is set to Explicit, which can't be used as Value no longer gets updated prior to a suitable time where UpdateTarget could be called (such as a TextInput handler). I have tried other methods such as explicitly updating the Text value from the bound Value, forcing an actual change to Value temporarily to invoke an update, but these "hacks" either don't work or cause other problems.
Any help would be greatly appreciated.
The code is below.
XAML snippet
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{local:NumberFormatConverter}"
UpdateSourceTrigger="Explicit"
Mode="TwoWay">
<Binding Path="Value"
RelativeSource="{RelativeSource AncestorType={x:Type Window}}"
Mode="TwoWay" />
</MultiBinding>
</TextBox.Text>
</TextBox>
C# snippet
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value", typeof(decimal), typeof(MainWindow),
new FrameworkPropertyMetadata(0m,
new PropertyChangedCallback(OnValueChanged)));
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
obj.SetValue(ValueProperty, Math.Round((decimal)args.NewValue, 1));
}
Converter class required
public class NumberFormatConverter : MarkupExtension, IMultiValueConverter
{
public static NumberFormatConverter Instance { private set; get; }
static NumberFormatConverter()
{
Instance = new NumberFormatConverter();
}
public override object ProvideValue(IServiceProvider serviceProvider_)
{
return Instance;
}
#region Implementation of IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
var result = 0m;
if (value != null)
{
decimal.TryParse(value.ToString(), out result);
}
return new object[] { result };
}
#endregion
}
I did a little digging on the Internet, and it turns out this was broken in WPF 4. Someone with an almost-identical problem to me posted here:
http://www.go4answers.com/Example/textbox-shows-old-value-being-coerced-137799.aspx
'Answer 8' states this was broken in WPF 4 and suggests a solution, which is to actually use UpdateSourceTrigger="Explicit" but to handle the TextChanged event and call BindingExpression.UpdateSource() to force changes in the text box to be reflected in the underlying value as if UpdateSourceTrigger="PropertyChanged", as per this post:
Coerce a WPF TextBox not working anymore in .NET 4.0
I implemented this, but lo and behold there were further side effects, in particular that every keystroke caused the caret to jump to the start of the text box due to updating the source and raising a PropertyChanged event. And also, any leading or trailing zeros or decimal places entered with the intention of entering further digits would get wiped out immediately. So, a simple condition to check the parsed decimal value of the text box versus the underlying value resolved this.
The following event handler is all that was needed:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var tb = (TextBox)e.Source;
MultiBindingExpression binding = BindingOperations.GetMultiBindingExpression(tb, TextBox.TextProperty);
decimal result = 0m;
decimal.TryParse(tb.Text, out result);
if ((decimal)GetValue(ValueProperty) != result && binding != null)
{
int caretIndex = tb.CaretIndex;
binding.UpdateSource();
tb.CaretIndex = caretIndex;
}
}

Struts2 using Map in select tag

You can easily use a List in struts2 select tag, but is there a way to use Map in tag?? If it is possible please provide a sample code...
thanx !
In my action class
public class MyAction extends ActionSupport {
private Map<String, String> map;
public String execute() throws Exception {
map = new HashMap<String, String>();
map.put("abc", "abc");
map.put("xyz", "xyz");
return SUCCESS;
}
}
For the jsp mapped to success, use some thing like this
<s:select list = "map" name = "name" label = "Name" headerKey="" headerValue = "Enter Value"/>
It depends on what are you trying to do. Lacking details, I can only point you to the docs : the list attribute of the select tag is an ...
Iterable source to populate from. If
the list is a Map (key, value), the
Map key will become the option 'value'
parameter and the Map value will
become the option body.
Below in the same doc there is an example with a (literal, inline) map (Months).

Resources