maui icon indicating copy to clipboard operation
maui copied to clipboard

Using TargetName reference in VisualStates in MAUI in C# (NOT Xaml)

Open quibenefacit opened this issue 3 years ago • 6 comments

I am posting it here at the recommendation of a Microsoft developer following my initial post on MAUI community support forum (https://learn.microsoft.com/en-us/answers/questions/1183810/using-targetname-reference-in-visualstates-in-maui)

Per Microsoft, it is possible to do MAUI development using C# only (no XAML). However, there appears to be no working C# equivalent to setting VisualStates which reference TargetName.

Here's an example. I need to make a label visible when a CollectionView item is selected, and invisible otherwise. This is easily accomplishable in XAML as follows:

<CollectionView.ItemTemplate>
   <DataTemplate x:DataType="sharedModels:Showuser">
       <Grid>
          <VisualStateManager.VisualStateGroups>
             <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal">
                   <VisualState.Setters>
                      <Setter TargetName="LabelCheckMark" Property="IsVisible" Value="False"/>
                   </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Selected">
                   <VisualState.Setters>
                      <Setter TargetName="LabelCheckMark" Property="IsVisible" Value="True"/>
                   </VisualState.Setters>
                </VisualState>
             </VisualStateGroup>
          </VisualStateManager.VisualStateGroups>
          <HorizontalStackLayout>
             <Label x:Name="LabelCheckMark" Text="X" />
             <Label Text="{Binding Username}" />
          </HorizontalStackLayout> 
       </Grid>
    </DataTemplate>
</CollectionView.ItemTemplate>

I am replicating this functionality in C# as follows. I create a helper grid class:

public class pickerGrid : Grid
{
    public pickerGrid() {
        var vsg = new VisualStateGroup() { Name = "vsg" };
        var vs_nor = new VisualState { Name = "Normal" };
        var vs_sel = new VisualState { Name = "Selected" };

        var LabelCheckMark = new Label {
            Text = "X",
         };

        var LabelName = new Label(); 
        LabelName.SetBinding(Label.TextProperty, "Name", BindingMode.Default, null, "{0}");

        vs_nor.Setters.Add(new Setter { TargetName = "LabelCheckMark", Property = IsVisibleProperty, Value = false });
        vs_sel.Setters.Add(new Setter { TargetName = "LabelCheckMark", Property = IsVisibleProperty, Value = true });

        var sl = new HorizontalStackLayout() {
            Children = {
                    LabelCheckMark,
                    LabelName,
            }
        };
        this.Add(sl);
        vsg.States.Add(vs_nor);
        vsg.States.Add(vs_sel);
        VisualStateManager.GetVisualStateGroups(this).Add(vsg);
    }
}

which I can then use for my CollectionView:

CollectionView _makeCollectionView() {
        var cV = new CollectionView();

        cV.SetBinding(ItemsView.ItemsSourceProperty, "ViewModel_Contacts");
        cV.SelectionMode = SelectionMode.Multiple;
        cV.SelectionChanged += OnSelectionChanged;

        cV.ItemsLayout = new GridItemsLayout(ItemsLayoutOrientation.Vertical) {
            VerticalItemSpacing = 6
        };
        cV.ItemTemplate = new DataTemplate(typeof(pickerGrid));
        return cV;
    }

and finally I use the created collection view in my window:

Content = new VerticalStackLayout {
            Children = {
                _makeCollectionView()
            }
};

However, the TargetName reference fails, because the "x:Name" property is not available in C#. The exception thrown is System.InvalidOperationException Message this element is not in a namescope.

quibenefacit avatar Feb 24 '23 06:02 quibenefacit

Hi @quibenefacit. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Feb 24 '23 08:02 ghost

Could you write a sample program of how you're trying to do this? If "The TargetName reference fails" I assume that means the VisualStateManager is throwing an exception? Or are you unable to write out the code?

drasticactions avatar Feb 24 '23 08:02 drasticactions

Hi, I have updated my initial post with the C# code and other information you have requested.

quibenefacit avatar Feb 24 '23 17:02 quibenefacit

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

ghost avatar Feb 28 '23 19:02 ghost

@quibenefacit if you look at the code here you'll see a demo of how-to setup your own namescopes.

PureWeen avatar Feb 28 '23 19:02 PureWeen

@quibenefacit if you look at the code here you'll see a demo of how-to setup your own namescopes.

Thank you, this works! I suggest converting this issue from needing implementation to needing documentation.

quibenefacit avatar Mar 01 '23 00:03 quibenefacit

In case anyone else will need it, the link changed to this

pr0skilled avatar Jun 22 '23 22:06 pr0skilled

if you look at the code here you'll see a demo of how-to setup your own namescopes.

In case anyone else will need it, the link changed to this

@PureWeen @pr0skilled

I have this issue as well, though i dont understand how what is linked resolves the issue, and certainly dont see a "demo" Please advise

axa88 avatar Sep 24 '23 10:09 axa88

Verified this issue with Visual Studio Enterprise 17.9.0 Preview 1.0. Can repro this issue.

homeyf avatar Nov 30 '23 05:11 homeyf

I run on a very similar issue just now, i could make it work. You need to set the namescope and then register the names, like this.

using Microsoft.Maui.Controls.Internals;
...
var label = new Label {
  Text = "X",
};

INameScope nameScope = new NameScope();
NameScope.SetNameScope(/*yout root view*/, nameScope);
//nameScope.RegisterName("Root", /*yout root view*/); //not necesary, but if u need it
nameScope.RegisterName("NameHere", label);
...

afther that you can use TargetName = "NameHere" on setters. You can also use .FindeByName("NameHere") method on the created view.

abelaegerter avatar Jul 23 '24 19:07 abelaegerter