-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Added the ability to Target a bindable object for Setters #5034
Added the ability to Target a bindable object for Setters #5034
Conversation
Include unit and xaml unit tests corrected naming to prevent issues Updating xamlc is still outstanding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this! A small, but powerful PR!
I just had a question on the changes from x:Name=""
to Name=""
- was that intentional?
I have another question... How does this work with styles? One of the things that I used this part of the VSM is to apply a style to all instances of a custom control. Then, I would change the state, and the manager would update sub views. Similar thing with templates - we don't have access to the actual instance at the time of creation. Not sure what this all means, but is a lookup for the element by name better than a reference? If this all works, then this is fine. An example case is say I have a control that I am using - either local or third party: <!-- LabelledEntry -->
<ContentView>
<StackLayout>
<Label Text="Enter text here:" />
<Entry Name="TextEntry" />
</StackLayout>
</ContentView> Then, on my page (or in the app) resources, I create a style: <Style TargetType="LabelledEntry">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Invalid">
<VisualState.Setters>
<Setter Target="TextEntry" Property="Entry.BorderColor" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style> Now, if I have this xaml: <ContentPage>
<LinearLayout x:Name="myControls">
<!-- our custom control where we injected our error state -->
<LabelledEntry Text="{Binding TheText}" />
<!-- a control that already supports error states -->
<ErrorSupportedEntry Text="{Binding TheText}" />
<!-- summary -->
<ErrorList IsVisible="false">
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="IsVisible" Value="Normal" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Invalid" />
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
</ErrorList>
</LinearLayout>
</ContentPage> In my code, I can just go to states: foreach (var entry in myControls.Children) {
VisualStateManager.GoToState(entry, isError ? "Invalid" : "Normal");
} |
@mattleibow That is not the separation of concerns that I would normally take. |
How does this work with the VSM added via Style? The same issue occurs, you don't have a reference to any objects - but you want to add a state. You could add the state to the actual control, but you loose the ability to have different state styles. The case I actually noticed this was with different screen sizes/orientation. When I was in a wide screen, I moved some elements around (which can use references), but then changed the layout of items in a list (which was added via data binding/templates). I could have created a control to wrap the items and their layout, but that means code. The whole point of XAML is to not write code for visual things. For example, here is a sample xaml file that defines some control and uses the visual state manager and styles for a certain look. When the screen goes wide, it changes: This example is a little weird in that I use n |
Great feedback. Thank you @mattleibow
To select which object to update, the search starts where the VSM is defined. i.e. Page level, control level, ... This may be a little more complex to document, but it should work for more cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setters are also used in Styles, how does the Target and TargetName properties behave in that case ?
needs to be targeted to |
Just slapped it with the I have removed Target from the Setter. If TargetName is present, then it will search the NameScope of the applied bindable object (basically child elements). @mattleibow I think this was your original preference |
I also liked the fact that a reference could be set... That might have been a performance boost - not requiring a search through the views. But then things get complicated for the user... But, we can also be extra clever and make the type object... If it is a string, we do a lookup. If it is a bind able object, we use that directly. Or, we can go the route of creating a new type that implicitly converts from both: class BindableTargetObject {
public string TargetName;
public BindableObject Target;
public BindableObject GetTarget(BindableObject scope) =>
Target ?? scope?.FindObject(TargetName) ?? scope;
} A bit more complex, but actually quite simple - especially since this works with Styles. |
More awesome feedback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking really good!
I was just thinking of OnPlatform. And then we have this big chunk of XAML that can totally be fixed:
https://github.com/xamarin/Xamarin.Forms/blob/bd31e1e9fc8b2f9ad94cc99e0c7ab058174821f3/Xamarin.Forms.Controls/GalleryPages/VisualStateManagerGalleries/ValidationExample.xaml
You probably can fit in a string, reference, style and OnPlatform (on the Target) into that one. I think those should cover the main thrust of the PR and we will have a visual confirmation.
BindableObject FindTargetObject(BindableObject scope, object target) | ||
{ | ||
if (scope == null || target == null) | ||
return scope; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? I think the next 2 if statements will catch any nulls. The only case that looks to change is if scope is null, but target is BindableObject. But, we are returning the object directly.
Hey @brainoffline thanks for your awesome work! We would like to get this in soon. Since you haven’t responded in some time, I’m going to assume that you do not have the possibility to finish it. @andreinitescu you’ve offered. Are you able to make some progress on this? Else we will take it ourselves |
Yes, I did offer, but I got no reply if I could actually start the work on it. |
Most of the work implementing |
@andreinitescu It looks like you may be able to get to this before us! Still willing? Thanks! |
Was this pushed to v4.4.0? https://github.com/xamarin/Xamarin.Forms/wiki/Feature-Roadmap still indicates v4.3.0 |
Thanks for asking @bezysoftware! The thing I can say for sure is that it won't be in 4.3 unfortunately. I will do my absolute best to get it into 4.4. The PR is mostly ready, so I think that should be possible :) |
closing in favor of #8144 |
* Added the ability to Target a bindable object for Setters Include unit and xaml unit tests corrected naming to prevent issues Updating xamlc is still outstanding * Resolve xamlc compiler with new Target property * Added ability to find target element via Name * Remove Target for simplicity * Additional target type for flexibility while not introducing complexity * [Core] Remove inline comment * Added gallery page * Last bits * Fixed unit tests * Update Setter.cs fixes #4924 closes #5034 closes #5622
Description of Change
Adds the ability to target a child object for a Setter for visual state management or Styles.
See VisualStateManagerTests.xaml for an example.
Issues Resolved
API Changes
Behavioral/Visual Changes
None
Before/After Screenshots
Not applicable
Testing Procedure
PR Checklist