none
Visibility property Binding

    Question

  • How can bind Rectangle Visibility property to WebContext.Current.User,IsAuthenticated? The only thing that I was able to make it sort of work is to expose IsAuthenticated property on UserControl, and then bind Path to this property. This is not working quite as I want it to, since I need this to change state when WebContext.Current.User,IsAuthenticated changes values.

    Is there some way I can bind to it directly in xaml?

    Thanks.

    Tuesday, December 27, 2011 3:10 PM

Answers

  • Another way to make it work out at design time would be sort of what I mentioned before.  You could make your own class which has a property whose getter gets the value from the WebContext object.  Then in the getter you could ADD some code that says at design time to either return true or false (which ever one you want to see at design time) and at run time return the actual WebContext value.

    Since YOUR class would be something that is not a static class, you could add YOUR class in the resource of your form.  After that, you should be able to reference the StaticResource at design time.

    You could even have your class add a dependencyproperty that you could set in the resource to true or false, whichever one you want to see at design time.  Then in the code, you could test for design time and use the dependency property instead of the WebContext value.  That way, you could test how things will look with it true or false at design time.

    Friday, December 30, 2011 8:02 AM

All replies

  • You would need to use a bool to visiblity converter, see here for an example:

    http://www.jeff.wilcox.name/2008/07/visibility-type-converter/

    Wednesday, December 28, 2011 8:38 AM
  • I already have this converter. In my first post I have said that I am able to bind to IsAuthenticated property (altough didnt menton that its of type bool), so this cvonversion works fine.

    However, I am unable to bind it to WebContext.Current.User.IsAuthenticated property. Maybe ther is a problem with it bening a singleton class (static property)?

    Wednesday, December 28, 2011 2:20 PM
  • Maybe try making your own property and have its getter return the IsAuthenticated.  Then bind to YOUR property.

    Perhaps you have tried to bind to "WebContext.Current.User.IsAuthenticated" with a DataContext of your own ViewModel and WebContext isn't part of your ViewModel.

    Hence my recommendation.

    Wednesday, December 28, 2011 2:24 PM
  • Binding to my custom property works, except that I loose property changed notification. This can be bypassed, but then I need to write too much code for a simple task. Simpler solution would be to subscribe to LoggedIn/LoggedOut events, but I am trying to keep it simple as possible, this is why I asked if its possible to directly bind to IsAuthenticated property of User class.

    I am not following you in your last sentence, can you elaborate more about it? What I have tried is making a static resource that points to WebContext class, and then tried to use it as Source for Binding. This action, however, tries to invoke contructor on WebContext, and there can be only one instance of it in application.

    There is an example on below site, which looks like what I am after, I have tested it with TextBlock and it works, but I just cant make it work with rectangle

    http://blogs.msdn.com/b/brada/archive/2009/10/05/business-apps-example-for-silverlight-3-rtm-and-net-ria-services-july-update-part-26-authentication-and-personalization.aspx

    This part in particular:

    1. <TextBlock Text="Data is only available to authenticated users" Foreground="Red"
    2.            DataContext="{StaticResource RiaContext}"
    3.            Visibility="{Binding Path=User.IsAuthenticated, Converter={StaticResource VisibilityConverter}}">
    4. </TextBlock>

    Wednesday, December 28, 2011 5:23 PM
  • Yes, all tags in xaml cause the constructor for that class to be called.  (which I REALLY like because all tags are just object classes).

    Not sure which "last sentence" you mean.  The "Hence my recommendation"?

    Make your getter for your user property get the value from the WebContext.

    Not sure why a Rectangle wouldn't work and the TextBlock does.  Would have been more interesting to see the one that DOESN'T work so we can maybe spot something wrong.

    Thursday, December 29, 2011 8:42 AM
  • Here is some xaml.  When I click the button and change thr property, all the objects appear or disappear.  The rectangle isn't any different:

        <Grid x:Name="LayoutRoot" Background="White">
          <Rectangle Width="100" Height="100" Fill="Yellow" Visibility="{Binding myTextVisibility}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
          <TextBlock Text="hello" Visibility="{Binding myTextVisibility}" />
          <Button Content="test" Click="Button_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,30,0,0"/>
       </Grid>

    Here is the code behind (in Delphi Prism language):

    namespace SilverlightApplication66;
    
    interface
    
       uses
          System,
          System.Windows;
    
       type
          MainPage = public partial class(System.Windows.Controls.UserControl)
             private
                method MainPage_Loaded(sender: Object; e: RoutedEventArgs);
                method Button_Click(sender: System.Object; e: System.Windows.RoutedEventArgs);
             public
                constructor;
                property myTextVisibility : Visibility; notify;
                end;
      
    implementation
    
       constructor MainPage; 
          begin
          InitializeComponent();
          Loaded += MainPage_Loaded;
          end;
    
       method MainPage.Button_Click(sender: System.Object; e: System.Windows.RoutedEventArgs);
          begin
          if myTextVisibility = Visibility.Visible
             then myTextVisibility := Visibility.Collapsed
             else myTextVisibility := Visibility.Visible;
          end;
    
       method MainPage.MainPage_Loaded(sender: Object; e: RoutedEventArgs);
          begin
          DataContext:= self;
          myTextVisibility := Visibility.Visible;
          end;
    
       end.

    Maybe you have the binding typed wrong or something for the rectangle.

    Thursday, December 29, 2011 8:58 AM
  • Actually this works, but I am getting warning that WebContext resource doesnt exist.

    1. <Border
    2.            DataContext="{StaticResource WebContext}"
    3.            Visibility="{Binding Path=User.IsAuthenticated, Converter={StaticResource VisibilityConverter}}" />

    If I remove DataContext attribute, it doesnt work. Can someone explainl me why and how to get rid of the "resource not exist" warning?

    Thursday, December 29, 2011 11:33 AM
  • WebContext is NOT a static resource.  A Resource is something that you create in a Resource section and give a x:key so that it can be located by binding such as StaticResource.

    So the way to do it is what I said before.

    Exactly how to go about that in the easiest fashion depends on how you are doing other things in your app.  For instance, are you using a ViewModel for your Page or View?  If so, add the property whose getter gets the IsAuthenticated to that ViewModel.  Then, assuming you are setting the Page DataContext to the ViewModel, the binding to YOUR property will work.

    But you can't reference a StaticResource that you don't create.  Which is what you are trying to do in line 2. above.

    Here is my code with the property added:

    namespace SilverlightApplication66;
    
    interface
    
       uses
          System,
          System.Windows;
    
       type
          MainPage = public partial class(System.Windows.Controls.UserControl)
             private
                method GetIsAuthenticated: boolean;
                method MainPage_Loaded(sender: Object; e: RoutedEventArgs);
                method Button_Click(sender: System.Object; e: System.Windows.RoutedEventArgs);
             public
                constructor;
                property myTextVisibility : Visibility; notify;
                property IsAuthenticated  : boolean read GetIsAuthenticated;
                end;
      
    implementation
    
       constructor MainPage; 
          begin
          InitializeComponent();
          Loaded += MainPage_Loaded;
          end;
    
       method MainPage.Button_Click(sender: System.Object; e: System.Windows.RoutedEventArgs);
          begin
          if myTextVisibility = Visibility.Visible
             then myTextVisibility := Visibility.Collapsed
             else myTextVisibility := Visibility.Visible;
          end;
    
       method MainPage.MainPage_Loaded(sender: Object; e: RoutedEventArgs);
          begin
          DataContext:= self;
          myTextVisibility := Visibility.Visible;
          end;
    
       method MainPage.GetIsAuthenticated: boolean;
          begin
          result := WebContext.Current.User.IsAuthenticated;
          end;
    
    end.

    Now you should be able to bind to IsAuthenticated and have it work.

    (I don't use RIA so I can't compile and test it, but it should work similar to that)

    Thursday, December 29, 2011 11:43 AM
  • In project created from Silvelight business application template, on Application_Startup event there is next line of code:

    this.Resources.Add("WebContext", WebContext.Current);

    So, WebContext is added as a static resource, which allows us to bind controls in XAML to WebContex.Current properties. Since we are adding it at runtime, hence the reason why I am getting a warning about it. When running the application everything works fine.

    If you look at the link I have provided (from msdn), you can see that MS uses the same method that I am using here. My preference is to keep anything related to UI in XAML, so this is the reason why I would like not to use code-behind (In my first post I have said that I know how to make it work in code-behind, by binding to custom property, which you suggest).

    Is there some catch to bypass this warning?

    Thursday, December 29, 2011 1:15 PM
  • Does changing the name of the resource from "WebContext" to something else avoid the conflict?

    Thursday, December 29, 2011 1:50 PM
  • No, the problem is not in the name conflict, but in the fact that IDE doesnt recognize this resource as it is added in c# code, not in XAML.

    Thursday, December 29, 2011 3:52 PM
  • Resources can be added in code.  The problem is probably that you are trying to add the resource AFTER the InitializeComponent call.  So, if the InitializeComponent call is made first, it tries to create all the objects in the xaml and then do the bindings (including the StaticResource).

    So if you put that Resources.Add BEFORE the InitializeComponent, it should be able to bind during the InitializeComponent portion.

    (It worked for me in code.  But not for the WebContext because I don't use Ria)

    Here is what I tried:

    namespace SilverlightApplication66;
    
    interface
    
       uses
          System,
          System.Windows;
    
       type
          MainPage = public partial class(System.Windows.Controls.UserControl)
             private
                method GetIsAuthenticated: boolean;
                method MainPage_Loaded(sender: Object; e: RoutedEventArgs);
                method Button_Click(sender: System.Object; e: System.Windows.RoutedEventArgs);
                property MyString : string := 'test';
             public
                constructor;
                property myTextVisibility : Visibility; notify;
                property IsAuthenticated  : boolean read GetIsAuthenticated;
                end;
      
    implementation
    
       constructor MainPage; 
          begin
          Resources.Add( 'mine', MyString );
          InitializeComponent();
          Loaded += MainPage_Loaded;
          end;
    
       method MainPage.Button_Click(sender: System.Object; e: System.Windows.RoutedEventArgs);
          begin
          if myTextVisibility = Visibility.Visible
             then myTextVisibility := Visibility.Collapsed
             else myTextVisibility := Visibility.Visible;
          end;
    
       method MainPage.MainPage_Loaded(sender: Object; e: RoutedEventArgs);
          begin
          DataContext:= self;
          myTextVisibility := Visibility.Visible;
          end;
    
       method MainPage.GetIsAuthenticated: boolean;
          begin
          result := true;
          end;
    
    end.

    And here is the xaml:

        <Grid x:Name="LayoutRoot" Background="White">
          <Rectangle Width="100" Height="100" Fill="Yellow" Visibility="{Binding myTextVisibility}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
          <TextBlock Text="hello" Visibility="{Binding myTextVisibility}" />
          <Button Content="test" Click="Button_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,30,0,0"/>
          <TextBlock Text="{StaticResource mine}" Margin="0,50,0,0"/>
       </Grid>
    

    Of course, the StaticResource may not be recognized in design-time, but it will at run-time.

    Thursday, December 29, 2011 9:20 PM
  • Of course, the StaticResource may not be recognized in design-time, but it will at run-time.

    This is the key problem from the beginning, since , as I said, its recognized at runtime, but not at design time. Currently call to add this resource in code is made in App.xaml (Application.Resources), since it should be available in whole app, not just one window.

    I have tried removing it from Application.Resources and adding it to MyPage.Resources before the InitializeComponent() call in constructor, and it still gives design warning,

    Friday, December 30, 2011 5:34 AM
  • OOOOhhhh.  Is it giving you an error dialog and not displaying in the designer?  Or is it just underlining the error or what?  If it is just underlining it in the designer, I would ignore the message.

    Friday, December 30, 2011 7:57 AM
  • Another way to make it work out at design time would be sort of what I mentioned before.  You could make your own class which has a property whose getter gets the value from the WebContext object.  Then in the getter you could ADD some code that says at design time to either return true or false (which ever one you want to see at design time) and at run time return the actual WebContext value.

    Since YOUR class would be something that is not a static class, you could add YOUR class in the resource of your form.  After that, you should be able to reference the StaticResource at design time.

    You could even have your class add a dependencyproperty that you could set in the resource to true or false, whichever one you want to see at design time.  Then in the code, you could test for design time and use the dependency property instead of the WebContext value.  That way, you could test how things will look with it true or false at design time.

    Friday, December 30, 2011 8:02 AM
  • I hate those warning messages, thats why I am trying to het rid of them. :) Having a custom class that would be a wrapper to User.IsAuthenticated property is probably the best solution, since I can reuse it everywhere.

    Thanks for all the help.

    Friday, December 30, 2011 8:04 PM
  • Hi, as I hate Warnings too, I bound to some Property in VM:

            private bool _isLogged;
            [Export("LoggedIn", typeof(Action<bool>))]
            public void SetUI(bool l)
            {
                IsLogged = l;
            }
            public bool IsLogged 
            { 
                get { return _isLogged; }
                set
                {
                    _isLogged = value; 
                    NotifyOfPropertyChange(()=>IsLogged);
                } // { return WebContext.Current.User.IsAuthenticated; }}
            }
    

    as you can see, there's MEF used; the exported Method is called in LoginStatus.xaml.cs

            [Import("LoggedIn", typeof(Action<bool>))]
            public Action<bool> Logged { get; set; }
            private void UpdateLoginState()
            {
                Logged(WebContext.Current.User.IsAuthenticated);
                if (WebContext.Current.User.IsAuthenticated)
                {
    ...

    As in generated Business App template.

    Thursday, January 05, 2012 4:17 AM
  • I didn't quite follow that (maybe because I don't use MEF).  But what is Logged?

    Thursday, January 05, 2012 8:44 AM
  • after [Import...]? Imported reference (delegate) to method SetUi implemented elsewhere and exported as "LoggedIn". You can see it in code attached above. It's just MEF.

    Thursday, January 05, 2012 8:50 AM
  • Okay, I see it now...  Thanks.

    Thursday, January 05, 2012 8:59 AM
  • Hi Greg,

    I am struggling a little with a basic scenario of disabling some controls based on the the authentication state, exactly as you have described above. I've followed your example and it all makes sense, so thanks, however, I am getting the following exception in the MainPage constructor where it creates the LoginStatus object:

    The invocation of the constructor on type 'MyApp.LoginUI.LoginStatus' that matches the specified binding constraints threw an exception. [Line: 73 Position: 24]

    I have noticed that I am using:

       this.NotifyPropertyChanged("IsLogged");

    rather than:

       NotifyOfPropertyChange(()=>IsLogged);

    in your example. 

    I believe you inherit from a base ViewModel where your would have this method defined, whereas I define NotifyPropertyChanged as follows directly but cannot see that this would cause a problem:

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
    if (PropertyChanged != null)
    {
    PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
    }
    
    
    (I am not using a ViewModel in my example I am setting the above properties in the code-behind my MainPage Xaml and LoginStatus.Xaml)
    
    
    Can you possibly shed any light please?
    Much appreciated,
    Steven
    
    
    
    
    Thursday, March 22, 2012 5:57 AM
  • Hi Steven,

    without a view of the class implementation, I cannot say why an exception is thrown. Your implementation and usage of PropertyChanged seems 100% correct. BTW my code uses Caliburn.Micro of Rob Eisenberg... therefore lambda.

    Cheers, Greg

    Thursday, March 22, 2012 12:53 PM
  • Maybe try moving the code OUT of the constructor and into a Loaded event handler.  Perhaps there is some sort of timing issue.

    And what was at line 73 position 24?

    And what exception did it throw?

    Did you set a break point and check to see if MyApp or LoginUI were null?

    Thursday, March 22, 2012 2:35 PM