One way to set an ImageSource for Image in XAML is like this:
<Image Width="75" Height="75">
<Image.Source>
<BitmapImage UriSource={Binding Uri} DecodePixelWidth="75" DecodePixelHeight="75" />
<Image.Source>
</Image>
This code contains a nice optimization, since a potentially large bitmap will be decoded to 75x75 pixels.
I'd like to be able to replace BitmapImage with my custom class like this:
<Image Width="75" Height="75">
<Image.Source>
<custom:PictureBitmap Picture={Binding Picture} Width="75" Height="75" />
<Image.Source>
</Image>
My application implements the Picture class, which maps to a database table. Picture class has everything I need to create an instance of BitmapImage. So PictureBitmap is essentially an adapter for the BitmapImage.
Here is how I started:
public class PictureBitmap : BitmapSource
{
// TODO: create Picture Dependency Property
// TODO: create a BitmapImage from Picture
// TODO: implement abstract methods by delegating calls to BitmapImage
}
Although BitmapSource is abstract, the API reference doesn't explain how to implement it.
Does anyone know how to feed a custom object to Image.Source?
My app supports Windows Phone Mango (7.5) and up.
Thanks!
Solved the problem by taking the attached properties approach.
To set the Source property on an Image using my custom logic, I do the following:
<Image Width="75" Height="75" my-namespace:PictureBitmap.Source={Binding Picture} />
This link turned out to be tremendously helpful: https://stackoverflow.com/a/16103494/1809457
Also, I noticed that DecodePixelWidth and DecodePixelHeight properties were not available in Mango.
Mango Provides the PictureDecoder class, however the drawback is that it must be used on the UI thread.
Related
The template is defined as:
<ContentPage.Resources>
<DataTemplate x:Key="MenuOptionTemplate">
<controls:MenuButtonControl />
</DataTemplate>
</ContentPage.Resources>
<ScrollView Orientation="Vertical">
<FlexLayout
AlignContent="Start"
AlignItems="Start"
BindableLayout.ItemTemplate="{StaticResource MenuOptionTemplate}"
BindableLayout.ItemsSource="{Binding MenuOptions}"
JustifyContent="SpaceEvenly"
VerticalOptions="Start"
Wrap="Wrap" />
</ScrollView>
The MenuButtonControl is define as:
...
<ImageButton
Source="{AppThemeBinding Light={Binding LightImageSource},
Dark={Binding DarkImageSource}}"/>
...
MenuOptions is is dynamically generated based on the user's role, but basically each menu option is create like so:
new MenuOption {
Title = "My Title",
LightImageSource = "sample_light",
DarkImageSource = "sample_dark"
}
{Binding LightImageSource} does not work.
So what is the correct way to implement this?
Because AppThemeBinding.Light is a markup extension (inherit from IMarkupExtension) property and not a BindableProperty you cannot use DynamicResource or Binding with it.
Thus you cannot use AppThemeBinding in this case. But you can use bindings (without AppThemeBinding), converters.. (see answers on the linked question), you just need to add the logic to conditionally set the appropriate image source based on the active theme using:
Application.Current.RequestedTheme;
Binding image source dynamically on xamarin forms
AppThemeBinding source
RequestedTheme
Edit
To react on theme changes use the event rather than testing the property RequestedThemeChanged:
How to detect Xamarin Forms System Theme Change
I have placed some elements in a grid without any Row & Column definitions.
<Grid>
<Button x:Name="button" Text="{Binding ButtonText}" CornerRadius="5" />
<Image HorizontalOptions="Start" VerticalOptions="Center" Source="{Binding IconImageSource}" InputTransparent="True"/>
</Grid>
Here, the image is the top element. When the button got tapped, the image goes to back. Why is this happening and is there any way to stop it?
This reason is from Fast Renderers .
Fast renderers are available for the following controls in Xamarin.Forms on Android:
Button
Image
Label
Frame
Functionally, these fast renderers are no different to the legacy renderers. From Xamarin.Forms 4.0 onwards, all applications targeting FormsAppCompatActivity will use these fast renderers by default.
Fast renderers can be overridden with the following approaches:
Enabling the legacy renderers by adding the following line of code to your MainActivity class before calling Forms.Init:
Forms.SetFlags("UseLegacyRenderers");
Using custom renderers that target the legacy renderers. Any existing custom renderers will continue to function with the legacy renderers.
Specifying a different View.Visual, such as Material, that uses different renderers. For more information about Material Visual, see Xamarin.Forms Material Visual.
So , here Solution is adding the following to your MainActivity.OnCreate before the global::Xamarin.Forms.Forms.Init(this, savedInstanceState) call:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
global::Xamarin.Forms.Forms.SetFlags("UseLegacyRenderers"); //add code here
global::Xamarin.Forms.Forms.Init (this, bundle);
LoadApplication (new App ()); // method is new in 1.3
}
The effect :
In my Xamarin xaml file I have a ProgressBar, where I need to compute Progress (Value/Maximum). So I wrote my Convertor, but I am not able to pass the Maximum to the Convertor.
I tried to use ConverterParameter, but it does not support bindings...
<ProgressBar Progress="{Binding Progress.Value,
Converter={StaticResource Convertor}, ConverterParameter=??}" />
Am I doing something wrong or is there any workaround?
You could pass a reference directly into your converter if it's defined in xaml of whatever class/ViewModel that has all the values you need to calculate progress.
<ProgressBar x:Name="_progressBar" Progress="{Binding Progress.Value,
Converter={StaticResource Convertor}, ConverterParameter={x:Reference _progressBar}}" />
or you could just pass in the value directly in xaml or as a static value defined in a static class from elsewhere in your app, which is of course if you know what the value will be ahead of time.
, ConverterParameter=1}" or
, ConverterParameter={x:Static local:DefaultValues.MaxValue}}"
However, it does sound like you could calculate Progress in your ViewModel on property changed before setting Progress.Value/Progress.MaxValue property, and then you wouldn't even need a converter.
In the Universal Windows Platform API, how do I use x:Bind inside of a User Control (intended to be the layout for a GridView's ItemTemplate) to bind to instance properties of a GridView's ItemSource?
Background
I'm trying to re-create the layout found in Windows 10 stock apps like Sports, News, Money, etc.
I'm using a two GridViews for the main area of the app; one for "featured articles" (2 large photos w/ headlines) and one for all the other articles (smaller photos w/ headlines).
I'm able to bind to a data source that I supply in the code behind (a List where NewsItem is a POCO with a Image and Headline property) Here's the pertinent parts of the MainPage.xaml:
<Page ...
xmlns:data="using:NewsApp.Models" />
....
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
....
The Image and Headline bind just fine (even though they've not been styled correctly). However, instead I think I need to bind to a User Control to get the styling options I want, control over resizing esp. when using Visual State Triggers and to simplify the XAML in general (at least, this was the technique suggested to me.)
So, I added a new User Control to the project (FeaturedItemControl.xaml), and copied in the DataTemplate's child Grid:
<UserControl ... >
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</UserControl>
And then back in the MainPage.xaml, I change the DataTemplate to reference the new FeaturedItemControl:
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<local:FeaturedItemControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
However, I get the error message for both Image and Headline properties: Invalid binding path 'Headline': Property 'Headline' can't be found on type 'FeaturedItemControl'.
I've tried a few things but am flailing just throwing code at the problem without understanding what I'm doing. Any help would be greatly appreciated.
Thank you for your kind attention.
Using Depechie's answer, I formulated this little cheat cheat for posterity:
Do note that you MUST use this technique to utilize the VisualStateManager with items inside your data bound controls' (GridView, ListView) data templates.
1) Create a User Control.
2) Cut the content of the DataTemplate in your page and paste it into the User Control replacing the template's Grid.
3) Reference the User Control from inside the Data Template:
4) Modify the contents of the User Control changing x:Bind statements to utilize object.property notation:
<UserControl>
<StackPanel>
<Image Source="{x:Bind NewsItem.LeadPhoto}" />
<TextBlock Text="{x:Bind NewsItem.Headline}" />
<TextBlock Text="{x:Bind NewsItem.Subhead}" />
</StackPanel>
</UserControl>
5) Add this in the User Control's Code Behind:
public Models.NewsItem NewsItem { get { return this.DataContext as Models.NewsItem; } }
public ContactTemplate()
{
this.InitializeComponent();
this.DataContextChanged += (s, e) => Bindings.Update();
}
Well it's possible to use x:Bind in user controls, but you'll need to add some extra code behind.
I encountered the same problem in my project, you can see the result here : https://github.com/AppCreativity/Kliva/tree/master/src/Kliva/Controls
So what you need to do is, create a property in the code behind of your user control that points to the correct DataContext.
If you do that, you can use properties of that DataContext in the xaml of your control: for example:
Do note that in the constructor of your control you do need to add: DataContextChanged += (sender, args) => this.Bindings.Update(); because the datacontext will change depending on the page where your control is used!
Then on the page where you are placing this control, you'll also need to do the same to enable the x:bind to work.
You'll see this in my example on the MainPage.DeviceFamily-Mobile.xaml and MainPage.xaml.cs files.
Hope this helps.
x:Bind isn't really hierarchical like Binding/DataContext is. Additionally when you're not directly inside a DataTemplate (such as inside your user control) the object that x:Bind tries to use is 'this' rather than 'this.DataContext'. My current line of thinking on how to solve this sort of issue is to try not to use UserControls anywhere. Instead preferring DataTemplates contained within a ResourceDictionary. There are some pretty strong caveats to this approach though, you will for example crash the xaml compiler if you use x:Bind inside a data template that was created from the ResourceDictionary item template (add new item). you can find a pretty complete example here https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlBind its important to note in the sample where they show the ResourceDictionary being used that its not actually just a ResourceDictionary.xaml its also a ResourceDictionary.xaml.cs (this is where the generated code from x:Bind ends up)
Another option is to add Headline and Image as properties on your user control and x:Bind them from the template, then inside the user control x:Bind as you are currently doing, but now the x:Bind generated path 'this.Headline' will exist. Unfortunately the order things are actually bound means that the x:Bind's you have inside your user control will have to be OneWay rather than the default OneTime. this is because x:Bind OneTime does the bind inside the InitializeComponent call, and any set of properties/DataContext stuff doesn't get done until after that has already run.
So to sum this up, you have two options, use data templates everywhere, or bind to properties that are directly on the user control.
I've read that Triggers are not supported in XAML for WP8. What's the alternative approach? I wanted to use a trigger to change the background image of a button when tapped.
You could, potentially just attach an event handler for the "Tap" event for the image. Without code examples, I'm not too sure how much I can help; however, I've pasted some code below:
XAML
<Image Source="/Assets/awesomeImg.png" Tap="AwesomeImg_Tap"/>
Code Behind (C#)
private void AwesomeImg_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
YourImageName.ImageSource = //code here to URI of image
}
Hope this helps!
Please try this.
Windows phone triggers msdn
Here I binded the image data trigger with my Boolean property , when the Boolean property is changed it will trigger and the setter will fire. Make sure you properties are implemented with INofityPropertyChanged
xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" x:Class="XXX_XXXX"
<Image Source="/Assets/Images/Tick.png"
Stretch="None"
HorizontalAlignment="Stretch"
VerticalAlignment="Top">
<interactivity:Interaction.Triggers>
<ec:DataTrigger Binding="{Binding IsTapped}" Value="True">
<ec:ChangePropertyAction PropertyName="Source">
<ec:ChangePropertyAction.Value>
<BitmapImage UriSource="/Assets/Images/Close.png"/>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</ec:DataTrigger>
</interactivity:Interaction.Triggers>
</Image>