How to bind device image path windows phone.
Below is image path
"C://Data//Users//Public//Pictures//Camera Roll//WP_20141001_002.jpg"
Thanks
I'm not sure if in your case using string is a good choice - maybe it will be possible to use BitmapImage - obtain a StorageFile from path, open Stream and then set BitmapImage - in this case you perform async operations outside converter.
In case you still want to use string it's possible, but will need some special approach - using async methods along with binding. There is a very good article about aynchronous MVVM, written by Stephen Cleary. Basing on the article and other Stephen's answer I've made such a code:
First of all, we will have to define a Converter - it's little complicated as getting file and stream is asynchronous:
/// <summary>
/// Converter getting an image basing upon delivered path
/// </summary>
public class PathToImage : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var task = GetImage((String)value);
// the below class you will find in Stephen's answer mentioned above
return new TaskCompletionNotifier<BitmapImage>(task);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{ throw new NotImplementedException(); }
private async Task<BitmapImage> GetImage(string path)
{
StorageFile file = await StorageFile.GetFileFromPathAsync(path);
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
BitmapImage image = new BitmapImage();
image.SetSource(stream);
return image;
}
}
}
In our page we will need a property, which we will use in binding and set the DataContext:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private string imagePath;
public string ImagePath
{
get { return imagePath; }
set { imagePath = value; RaiseProperty("ImagePath"); }
}
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
// rest of the code
Of course we have to define our binding - for example in XAML, it's little tricky as first we have to bind the DataContext to our Task then bind Source to the Result, which will be raised as the image is loaded:
<Image DataContext="{Binding ImagePath, Converter={StaticResource PathToImage}}" Stretch="Uniform"
Source="{Binding Result} HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
Once we have this all, we can set the property like this:
ImagePath = #"C:\Data\Users\Public\Pictures\Camera Roll\WP_20141001_002.jpg";
and we should see the result on the screen.
Related
We are using Xam.Plugin.HtmlLabel plugin in our xamarin forms application. When we set the html string in constructor, the html label is loading correctly in ios. But we assign the same label in method and called the method in constructor it is not loading in iOS and in android it's working fine. The issue reproduced code snippet is mentioned below. This issue is occurred only in iOS.
HTMLPage.xaml
<ContentPage.Content>
<Grid>
<ScrollView
Padding="24"
HorizontalScrollBarVisibility="Never"
VerticalScrollBarVisibility="Never">
<htmlLabel:HtmlLabel
Text="{Binding HTMLDescription}"
LinkColor="{StaticResource LinkColor}"
TextColor="{StaticResource PriTextColor}" />
</ScrollView>
</Grid>
</ContentPage.Content>
HTMLPage.xaml.cs
private string htmlDescription;
public string HTMLDescription
{
get { return htmlDescription; }
set { htmlDescription = value; OnPropertyChanged(); }
}
public HTMLPage(string Description)
{
InitializeComponent();
GetHTML(Description);
BindingContext = this;
}
private async void GetHTML(string description)
{
await Task.Delay(2000); //This delay is for getting the data from server.
HTMLDescription = description;
}
In whatever code does new HTMLPage("this is some html"), are you on the UI thread?
If not on UI thread, then that's your problem - dealing with UI elements off the UI thread is problematic.
If running on UI thread, then you have a different problem: a constructor is a "blocking" operation - it does no good to have async/await on code called inside a constructor; UI thread is blocked until the constructor returns! In general, its a bad idea to do anything lengthy there. Worst case, web query might delay until timeout.
Instead, try setting HTMLDescription AFTER page has appeared:
// Hold it until used.
string Description;
public HTMLPage(string description)
{
this.Description = description;
...
}
protected override void OnAppearing()
{
base.OnAppearing();
// Move to background, so OnAppearing can return.
Task.Run(() => {
// Potentially long operation.
var html = GetHTML(Description);
// Move to UI thread, before touching any UI element.
Device.BeginInvokeOnMainThread(() => {
HTMLDescription = html;
}
}
}
private string GetHTML(string description)
{
Task.Delay(2000); //This delay is for getting the data from server.
return description;
}
NOTE: I've removed async/await from this version of GetHTML, because it is only called on a background thread. You can put those back in, if desired.
Of course the downside of this, is that the page appears at first without that label. If you don't want that, then you need to instead GetHTML(...) BEFORE calling the constructor.
So your code (in a place you don't show) would be something like this:
var html = GetHTML(...);
new HTMLPage(html);
Thus, you are back to your original case that works, where in the constructor you already have the html string, so can simply do
HTMLDescription = description;
Which begs the question: why didn't you do that in the first place? Why did you put the call to GetHtML inside the constructor?
Although I'm relatively new to AutoMapper I'm using it in a small project I'm developing. I've never had problems using it before but now I'm facing some weird behavior passing parameters to a Custom Resolver.
Here's the scenario: I get a list of messages from my repository and then map those to a frontend friendly version of it. Nothing fancy, just some normal mapping between objects. I have a field in that frontend object that tells if a certain user already voted for that message and that's what I'm using the Custom Resolver for (it's that second "ForMember"):
public List<SupportMessageUi> GetAllVisible(string userId)
{
Mapper.CreateMap<SupportMessage, SupportMessageUi>()
.ForMember(dest => dest.Votes,
opt => opt.ResolveUsing<SupportMessageVotesResolver>())
.ForMember(dest => dest.UserVoted,
opt => opt.ResolveUsing<SupportMessagesUserVotedResolver>()
.ConstructedBy(() => new SupportMessagesUserVotedResolver(userId)));
var messages = _unitOfWork.MessagesRepository.Get(m => m.Visible);
var messagesUi = Mapper.Map<List<SupportMessageUi>>(messages);
return messagesUi;
}
I'm calling this method on a web service and the problem is: the first time I call the webservice (using the webservice console) it all runs perfectly. For example, if I pass '555' as the userId I get to this method with the correct value:
And in the Custom Resolver the value was correctly passed to the constructor:
The results returned are correct. The problem comes next. The second time I call the service, passing a different argument ('666' this time) the argument that gets to the constructor of the Custom Resolver is the old one ('555'). Here's what I mean:
Right before mapping the objects we can see that the value passed to the constructor was correct ('666'):
But when it gets to the constructor of the Resolver the value is wrong, and is the old one ('555'):
All subsequent calls to the service use the original value in the Custom Resolver constructor ('555'), independently of the value I pass to the service (also happens if I make the call from another browser). If I shut down the server and relaunch it I can pass a new parameter (that will be used in all other calls until I shut it down again).
Any idea on why this is happening?
It's happening because AutoMapper.CreateMap is a static method, and only needs to be called once. With the CreateMap code in your web method, you're trying to call it every time you call that method on your web service. Since the web server process stays alive between calls (unless you restart it, like you said) then the static mappings stay in place. Hence, the necessity of calling AutoMapper.Reset, as you said in your answer.
But it's recommended that you put your mapping creation in AppStart or Global or a static constructor or whatever, so you only call it once. There are ways to call Map that allow you to pass in values, so you don't need to try to finesse things with the constructor of your ValueResolver.
Here's an example using a ValueResolver (note the change to implementing IValueResolver instead of inheriting ValueResolver<TSource, TDestination>):
[Test]
public void ValueTranslator_ExtraMapParameters()
{
const int multiplier = 2;
ValueTranslator translator = new ValueTranslator();
Mapper.AssertConfigurationIsValid();
ValueSource source = new ValueSource { Value = 4 };
ValueDest dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(8));
source = new ValueSource { Value = 5 };
dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(10));
}
private class ValueTranslator
{
static ValueTranslator()
{
Mapper.CreateMap<ValueSource, ValueDest>()
.ForMember(dest => dest.Value, opt => opt.ResolveUsing<ValueResolver>().FromMember(src => src.Value));
}
public ValueDest Translate(ValueSource source, int multiplier)
{
return Mapper.Map<ValueDest>(source, opt => opt.Items.Add("multiplier", multiplier));
}
private class ValueResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
return source.New((int)source.Value * (int)source.Context.Options.Items["multiplier"]);
}
}
}
private class ValueSource { public int Value { get; set; } }
private class ValueDest { public int Value { get; set; } }
And here's an example using a TypeConverter:
[Test]
public void TypeTranslator_ExtraMapParameters()
{
const int multiplier = 3;
TypeTranslator translator = new TypeTranslator();
Mapper.AssertConfigurationIsValid();
TypeSource source = new TypeSource { Value = 10 };
TypeDest dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(30));
source = new TypeSource { Value = 15 };
dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(45));
}
private class TypeTranslator
{
static TypeTranslator()
{
Mapper.CreateMap<TypeSource, TypeDest>()
.ConvertUsing<TypeConverter>();
}
public TypeDest Translate(TypeSource source, int multiplier)
{
return Mapper.Map<TypeDest>(source, opt => opt.Items.Add("multiplier", multiplier));
}
private class TypeConverter : ITypeConverter<TypeSource, TypeDest>
{
public TypeDest Convert(ResolutionContext context)
{
TypeSource source = (TypeSource)context.SourceValue;
int multiplier = (int)context.Options.Items["multiplier"];
return new TypeDest { Value = source.Value * multiplier };
}
}
}
private class TypeSource { public int Value { get; set; } }
private class TypeDest { public int Value { get; set; } }
Answering myself: I was not using AutoMapper.Reset(). Once I did that everything started working properly.
Helpful reading: http://www.markhneedham.com/blog/2010/01/27/automapper-dont-forget-mapper-reset-at-the-start/
We are writing a Windows 8.1 Store App that uses the new SearchBox XAML control. It looks like the only way to get suggestions into the dropdown list as the user types is to use the SearchBoxSuggestionsRequestedEventArgs and get the SearchSuggestionCollection from the event then append the suggestions to that.
We're using Prism for WinRT and want to separate the SearchBox and it's events from the ViewModel that is getting the list of suggestion strings.
I can't find anyway of binding a list of strings to the SearchSuggestionCollection or any way of adding them programatically that doesn't involve using the event args, which is making out unit testing very complex.
Is there a way of binding/adding the suggestions that doesn't involve the event args?
Okay, so I got obsessed with this question, and here is a solution for when using the SearchBox. I've uploaded a full sample on MSDN and GitHub
In short, use the Behavior SDK and and the InvokeCommand, and then use a converter to grab whatever data you need by using the new attributes InputConvert and InputConverterParameter.
XAML:
<SearchBox SearchHistoryEnabled="False" x:Name="SearchBox" Width="500" Height="50">
<SearchBox.Resources>
<local:SearchArgsConverter x:Name="ArgsConverter"/>
</SearchBox.Resources>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SuggestionsRequested">
<core:InvokeCommandAction
Command="{Binding SuggestionRequest}"
InputConverter="{StaticResource ArgsConverter}"
InputConverterLanguage="en-US"
InputConverterParameter="{Binding ElementName=SearchBox, Path=SearchHistoryEnabled}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</SearchBox>
Converter:
public sealed class SearchArgsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var args = (SearchBoxSuggestionsRequestedEventArgs)value;
var displayHistory = (bool)parameter;
if (args == null) return value;
ISuggestionQuery item = new SuggestionQuery(args.Request, args.QueryText)
{
DisplayHistory = displayHistory
};
return item;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
}
Mainpade codebehind - of course you want this in a VM :)
public sealed partial class MainPage
{
public DelegateCommand<string> Search { get; set; }
public DelegateCommand<ISuggestionQuery> SuggestionRequest { get; set; }
public MainPage()
{
InitializeComponent();
Search = new DelegateCommand<string>(SearchedFor, o => true);
SuggestionRequest = new DelegateCommand<ISuggestionQuery>(SuggestionRequestFor, o => true);
DataContext = this;
}
private void SuggestionRequestFor(ISuggestionQuery query)
{
IEnumerable<string> filteredQuery = _data
.Where(suggestion => suggestion.StartsWith(query.QueryText,
StringComparison.CurrentCultureIgnoreCase));
query.Request.SearchSuggestionCollection.AppendQuerySuggestions(filteredQuery);
}
private readonly string[] _data = { "Banana", "Apple", "Meat", "Ham" };
private void SearchedFor(string queryText)
{
}
}
I wrote up a full walk through on my blog, but the above is all you really need :)
Is there a way to make DataContractJsonSerializer emit the "odata.type" field required when posting an OData entity into a collection that supports multiple entity types (hierarchy per table)?
If I construct DataContractJsonSerializer with a settings object with EmitTypeInformation set to Always, it emits a "__type" field in the output, but that's not the field name needed for OData and the format of the value is wrong as well.
Is there any way to hook into the DataContractJsonSerializer pipeline to inject the desired "odata.type" field into the serialization output?
It would be such a hack to have to parse the serialization output in order to inject the field. How does WCF Data Services do it? Not using DataContractJsonSerializer is my guess.
Have you considered using Json.Net? Json.Net is much more extensible and the scenario that you have can be done using a custom resolver. sample code
class Program
{
static void Main(string[] args)
{
Console.WriteLine(
JsonConvert.SerializeObject(new Customer { Name = "Raghu" }, new JsonSerializerSettings
{
ContractResolver = new CustomContractResolver()
}));
}
}
public class CustomContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract objectContract = base.CreateObjectContract(objectType);
objectContract.Properties.Add(new JsonProperty
{
PropertyName = "odata.type",
PropertyType = typeof(string),
ValueProvider = new StaticValueProvider(objectType.FullName),
Readable = true
});
return objectContract;
}
private class StaticValueProvider : IValueProvider
{
private readonly object _value;
public StaticValueProvider(object value)
{
_value = value;
}
public object GetValue(object target)
{
return _value;
}
public void SetValue(object target, object value)
{
throw new NotSupportedException();
}
}
}
public class Customer
{
public string Name { get; set; }
}
I can't answer your first two questions, but for the third question, I found on the OData Team blog a link to the OData WCF Data Services V4 library open source code. Downloading that code, you will see that they perform all serialization and deserialization manually. They have 68 files in their two Json folders! And looking through the code they have comments such as:
// This is a work around, needTypeOnWire always = true for client side:
// ClientEdmModel's reflection can't know a property is open type even if it is, so here
// make client side always write 'odata.type' for enum.
So that to me kind of implies there is no easy, clean, simple, elegant way to do it.
I tried using a JavaScriptConverter, a dynamic type, and other stuff, but most of them ended up resorting to using Reflection which just made for a much more complicated solution versus just using a string manipulation approach.
In v3 if I wanted to pass two objects to another viewmodel:
public class Dog
{
}
public class Cat
{
}
var dog = new Dog();
var cat = new Cat();
ShowViewModel<SomeViewModel>(new {Dog = dog, Cat = cat });
public class SomeViewModel
{
Init(Dog dog, Cat cat)
{
}
}
As far as I can tell that won't work because the types aren't recognized and can't be stuck in a dictionary. If I wanted to have these serialized as json, passed to the view model, and deserialized as Init parameters, would I implement IExtraParser? And if that is correct, how do I go about adding the implementations to the ExtraParsers dictionary?
update:
This seems to do it:
var foo = Mvx.Resolve<IMvxFillableStringToTypeParser>();
foo.ExtraParsers.Add(new MyParser());
The default navigation mechanism in MvvmCross is deliberately lightweight.
It is really there to allow you to pass just one simple, serializable object - e.g.
public class DogNav
{
public int Id {get;set;}
public string Caption {get;set;}
}
// received in:
public class DogViewModel : MvxViewModel
{
public void Init(DogNav dogNav)
{
}
}
With this setup, if a navigation is triggered like:
// navigation
ShowViewModel<DogViewModel>(new DogNav() { Id=12, Caption="Good boy" });
then the underlying system will transport the values from that DogNav object - possibly using Uris, Intents or other serialization techniques - to the new DogViewModel and will then ensure Init is called with the correct values.
Because of the serialization, it's important:
not to pass big objects (Uris on WindowsPhone can break above a few hundred characters)
not to expect the same object instance to arrive - i.e. if you are using database-backed or stateful objects, then it's best to pass some kind of lookup key rather than the objects themselves.
not to expect that only one ViewModel will receive the message - on some operating systems, it may be that the user goes back and forth many, many times between apps causing many Views and ViewModels to be created.
not to expect that a ViewModel that receives the message is in the same process and memory space as the ViewModel that sent the request - the same may actually be received days later after a tombstoning event.
If you do want to pass multiple objects via navigation, then I think you can do this using code like:
public class CatNav
{
public int CatId {get;set;}
public string CatCaption {get;set;}
}
public class DogNav
{
public int DogId {get;set;}
public string DogCaption {get;set;}
}
// received in:
public class CatAndDogViewModel : MvxViewModel
{
public void Init(DogNav dogNav)
{
}
public void Init(CatNav catNav)
{
}
}
In this case you could navigate using:
var catNav = new CatNav() { CatId =12, CatCaption="Meow" };
var dogNav = new DogNav() { DogId =12, DogCaption="Woof" };
var bundle = new MvxBundle();
bundle.Write(catNav);
bundle.Write(dogNav);
ShowViewModel<CatAndDogViewModel>(bundle);
I think this would work...
However... please be aware that the serialization is very simple - so if CatNav and DogNav were to share a property name, then this would lead to problems - you'd end up with some Cags and Dots
Because of the Cag and Dot problem I don't recommend this approach...
If you do need more complex transitions in your apps, then one route is to:
UPDATE - see Passing complex navigation parameters with MvvmCross ShowViewModel
1. Add the Json Plugin (or any Json serializer) and change your Setup.cs code to create a MvxJsonNavigationSerializer - overriding CreateNavigationSerializer
protected override IMvxNavigationSerializer CreateNavigationSerializer()
{
return new MvxJsonNavigationSerializer();
}
Use a composite object in navigation like:
public class DogAndCatNav
{
public DogNav DogNav {get;set;}
public CatNav CatNav {get;set;}
}
This would be received by:
public void Init(DogAndCatNav dogAndCatNav)
{
}
But note that this technique does need a more powerful serialization engine - such as Json.
Overall... even after writing all this... I'd recommend you pass as little data as possible in your navigations!