I am in the process of making a custom horizontally scrollable ListView type control, I have come across an issue which I can't explain, and I'm not 100% sure that the issue is even with my code, unless I have misunderstood something.
I have distilled the issue into the simplest form I can, available at https://github.com/sparkeh9/XamarinFormsListViewIssue.
So, I have a HorizontalListView control utilising a very simple data template selector (no logic, returns a single data template)
See XAML screenshot
See Template Selector screenshot
My issue is, when I use a template selector, when calling ItemTemplateSelector.CreateContent() in order to start generating UI controls based on the template, it throws an exception See exception screenshot which claims that the LoadTemplate property should not be null. I have looked at the base classes and found that this property is a Func<object> which is private, and can only be set from outside by calling a specific constructor.
If I specify a literal item template in the XAML, everything works as expected, such as:
<controls:HorizontalListView.ItemTemplate>
<DataTemplate>
<Label Text="test"></Label>
</DataTemplate>
</controls:HorizontalListView.ItemTemplate>
EDIT: Answered my own question
I figured out what I had done wrong - if the type is DataTemplateSelector, then you must first generate a template by calling ItemTemplateSelector.SelectTemplate( item, null ), after which point you can call CreateContent()
diff
I've committed this to demonstrate what I mean:
https://github.com/sparkeh9/XamarinFormsListViewIssue/commit/f2f0d807d2d463d1fe9e6a89f0d3c0c7676a0761
Related
I'm working on a solution to a localization problem. This isn't the normal language localisation.
<Label Content="{Binding myDictionary[A Test], FallbackValue=A Test}"/>
In practice the above code calls into the Dictionary in the view model, which is declared as
public Dictionary<string, string> myDictionary
The problem is that I have to define the string "A Test" twice in the label.
once as in the index in the binding, and again in the FallbackValue.
What I would like to end up with is something that looks like this...
<Label Content="{Binding myDictionary[A Test]}"/>
At the moment what happens when I do this is that the Xaml designer in Visual studio can't resolve myDictionary (as it won't know the datacontext so can't hook up to the viewmodel where it's defined) this means that the label will display as blank, which won't will make visual design harder.
I've looked into calling a static method however for myDictionary to function properly, it needs to be instantiated in the view model.
Is there a way of having either the index value "A Test" show up in the designer without having to use a fallback value?
The goal is to be able to have the content refreshed if the value in myDictionary[A Test] is updated (In reality myDictionary is observable)
as it won't know the datacontext so can't hook up to the viewmodel where it's defined
Not so.... One can use a design time only context by specifying it in the page's meta data atttributes such as:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{d:DesignInstance {x:Type viewModels:MainVM}}"
by doing that you can setup a design time only dictionary to use.
Read about here in MSDN magazine:
MVVM - Maximizing the Visual Designer’s Usage with Design-Time Data
Note one can still use the Blend namespaces in non Blend editors such as Visual Studio. Otherwise don't be afraid to use/learn Blend as needed.
If from your actual VM with real data, in design mode its good to ignore certain objects/actions which may cause issues. Here is a check to determine if the action is in design mode, if it is not then it executes the block, otherwise it is ignored because it is in design mode.
if (!DesignerProperties.GetIsInDesignMode(this))
Best way to handle this IMO is with a custom markup extension placed within a custom namespace, it allows you to write XAML like this:
<TextBlock Text="{Translate 'Hello World!'} />
What the Translate extension class returns is entirely up to you, typically you have it do a look-up into a Dictionary using the provided string as the index and, if it doesn't find it, just returns the string itself.
One of the nice things about this particular implementation is that it adds a listener to the Translation manager for every binding that is made, so they all automatically update at run-time whenever the current language is changed.
The one thing you do have to keep in mind though is to place the Translate extension class itself within its own project. Not sure why, it seems to be a bug in VS, but XAML files don't seem to be able to use custom markup extensions declared in the same project.
I have a used a control, say ListView, in my custom control. When i run my application i could see that ListView is not rendered in a different way. I suspect that somewhere a control template is defined for listview/listviewitem which resulted in this behavior. I checked the Template property of listview at runtime where i could see childitems are different.
Is there a way to find out that where is this template defined? I understand this sounds like a naive question and i should go thru all the resources and see where i could be present. I tried that but bcoz of complexity/number of the resource items i could not find anything useful.
Thanks
I have a template assigned to a button:
<Button x:Name="btn_PatMat" Template="{StaticResource PatMat_Button}" ...
How can I retrieve the Key/String/Name of this template from said button?
Pseudocode:
String = btn_PatMat.Template.???.ToString()
You can't. At least not in the way you're trying.
To quote from this SO post about x:Key (emphasis mine):
x:Key is used for items that are being added as values to a
dictionary, most often for styles and other resources that are being
added to a ResourceDictionary. When setting the x:Key attribute,
there is actually no corresponding property on the object or even an
attached dependency property being set. It is simply used by the
XAML processor to know what key to use when calling Dictionary.Add.
StaticResources are evaluated during loading, so once the control loads, the Template property is no longer set to a binding, but is instead set to a copy of the ControlTemplate from your Resources, and no corresponding property on that object is set to the key.
You can verify this by checking out the XAML of the button after it's loaded, by using something like XamlWriter.Save to view it's XAML string.
The only solution I can think of that might work would be to loop through your .Resources, and find a ControlTemplate that is equal to your Button's ControlTemplate. I haven't tested this, and it probably isn't very practical for large resource libraries, but it may be an option.
But a better solution would probably be to change your logic so the key value can be accessed some other way by whatever object needs it.
Well I'm afraid that's not possible because it's not intended by WPF. There are some people which wanted to get access to x:Name which is similar to x:Key, they all had to give up.
Pls have a look at this SO post and this additional link.
The only workaround I could imagine is reading all templates from the ResourceDictionary, instantiate each resource (if possible), find the template (if it's e.g. a style) and compare it with the current instance of the template found in the control. But this seems to be a pretty ugly solution and I'm not sure if it'll work without any problems.
The title is pretty self-explanatory. I cannot for the life of me, figure out how to load the default template for a particular object from within a DataTemplateSelector.
I know you're gunna ask. Why the heck would you need to do this? WPF will select that template for you if you bind the object to something!
Well, the short answer is: I need to make sure that every DataTemplate that gets returned is a new instance of the template. The control I have this template bound to is reusing it. I am trying to use x:Shared="False" but the control itself doesn't try to obtain the resource again, it's just reusing the one it already has. However, when I use a DataTemplateSelector, the SelectTemplate function get's called each time it requests a fresh UI. So if I can ensure that the DataTemplate being returned here is a new instance, everything should be great.
Except, as long as I am using a default template (ex, no x:Key="" attribute).
I don't know how to acquire that resource.
Thanks.
I have a control that we could identify as similar to ListBox control. Each item is represented with one element (example TextBlock). What i would like is to change the layout of this item, so that it contains two TextBlocks. So I create a ControlTemplate, put a Border Grid, TwoTextBlocks, and all is well. Now the problem:
I need to be able to localize the text in the item, and I did this normally like this:
<... Text="{Binding Strings.SomeString, Source={StaticResource ApplicationResources}}" />
Now I need to be able to do the same with both TextBlocks. So i thought I need to create a custom type that this item will bind to, and expose two propertiws: Title and Description. If I expose this properties as string type, everything works ok, but I am loosing markup binding that I used previously. How to achieve the same with two properties? The result should be like:
<... Title="{Binding Strings.SomeString, Source={StaticResource ApplicationResources}}", Description="{Binding Strings.AnotherString, Source={StaticResource ApplicationResources}}" />
I was able to make Localization work with ResourcemManager class, but it gets even complicated in order to provide localization to be applied dynamically at runtime.
So, what do I need to do to be able to use above code? Then I just need to implement INotifyPropertyChanged on ApplicationResource and all is set.
Great!
I'm going to do the same thing you did here. Yeah, I have a solution but I'm not sure if it works till now.
First, we need a LocalizationManager which holds a dictionary.
For example, if you need to localize a user account window, just do this
<TextBlock Text="something, UsernameKey">
And the localizationManager will map UsernameKey to "Username" or other language
Second, a xaml extension which find the value of the key from LocalizationManager.
I wonder if this custom extension could derived from Binding extension, if so, this'll be very easy, just create a Binding Object to the target. If not, I think holding a WEAK reference to the UIElement by xaml extension to dynamic update the text is proper.
This solution is simple but not generic. There're some language read from right to left. It asks the application to show content from right to left.
So, I have another generic solution but more complex.
Instead of xaml extension, we use an attach dependency property.
Do it like this:
<TextBlock LocalizationManager.LocalizationKey="UsernameKey" />
So, the problem now is how to set "Text" property by LocalizationManager?
We use adapters, LocalizationManager will search proper adapter for type "TextBlock"
So, when the application is booting, we register some adapters to LocalizationManager:
LocalizationManager.Current.RegisterAdapter<TextBlock>(new TextBlockAdapter())
This solution is more generic, it supports any kind of control if you provide adapter, but as you see, this solution needs more work and much more complex than the former one.
I hope these design solutions could help you~