I am trying to create an application bar in code for WinPhone7. The XAML that does it goes like this:
<PhoneApplicationPage.ApplicationBar>
<shellns:ApplicationBar Visible="True" IsMenuEnabled="True">
<shellns:ApplicationBar.Buttons>
<shellns:ApplicationBarIconButton IconUri="/images/appbar.feature.search.rest.png" />
</shellns:ApplicationBar.Buttons>
</shellns:ApplicationBar>
</PhoneApplicationPage.ApplicationBar>
So I thought I'd just rewrite it in C#:
var appbar = new ApplicationBar();
var buttons = new List<ApplicationBarIconButton>();
buttons.Add(new ApplicationBarIconButton(new Uri("image.png", UrlKind.Relative));
appbar.Buttons = buttons; //error CS0200: Property or indexer 'Microsoft.Phone.Shell.ApplicationBar.Buttons' cannot be assigned to -- it is read only
The only problem is that Buttons property does not have a set accessor and is defined like so:
public sealed class ApplicationBar {
//...Rest of the ApplicationBar class from metadata
public IList Buttons { get; }
}
How come this can be done in XAML and not C#? Is there a special way that the objects are constructed using this syntax?
More importantly, how can I recreate this in code?
appbar.Buttons.Add(new ApplicationBarIconButton(new Uri("image.png", UrlKind.Relative));
Add directly to the Buttons property.
It probably uses Buttons.Add instead of assigning to the Buttons property.
The ApplicationBar.Buttons member has an Add function (see this)
var appBarButton =
new ApplicationBarIconButton(new Uri("image.png", UrlKind.Relative)
appBar.Buttons.Add(appBarButton);
Related
I'm making app with using Xamarin.forms.
I already asked question here.
How to set child of class' property with using xaml? (Xamarin.forms)
But I couldn't get right answer for this, or there may be no solution for that.
What I want to do is setting my class's view's property from ContentPage's XAML.
my class has some view like Image and else.
I searched and found that there is 'ControlTemplete'. But I'm not sure it's what I'm looking for.
And I also don't think putting BindableProperty and OnPropertyChangedDelegate codes for every property that I want to set is a best way.
Is there another better solution?
Thanks.
You can map XAML that is inside your control to a property using ContentProperty attribute.
[ContentProperty("MyContent")]
public class MyControl : ContentView
{
public View MyContent { get; set; }
}
And in XAML somthing like this
<local:MyControl>
<Grid></Grid>
</local:MyControl>
this limits you to only one property but should work with any types.
In Xaml I can set a custom attached property using
local:TestClass.TestProperty="1"
An I can bind to a custom attached property using
{Binding Path=(Namespace:[OwnerType].[PropertyName])}
{Binding Path=(local:TestClass.TestProperty)}
But how do I specify the namespace when I need to use a custom attached property in a SortDescription?
I can bind to an attached property using
new SortDescription("(Grid.Row)", ListSortDirection.Descending)
but here I can't set a namespace anywhere...
Best Regards,
Jesper
You can't, for the same reason that {Binding Path=a:b.c} works but {Binding a:b.c} doesn't: The PropertyPath constructor has no namespace context.
Unfortunately in the case of SortDescription there isn't much you can do about it. You have to find a way to sort without using attached properties.
Normally I tell people that use of Tag is an indicator of bad coding, but in this case Tag may be your best option: You can create an object within Tag that has properties that return the actual attached properties you want.
In your PropertyChangedCallback, instantiate Tag to an instance of an inner class:
public class TestClass : DependencyObject
{
... TestProperty declaration ...
PropertyChangedCallback = (obj, e) =>
{
...
if(obj.Tag==null) obj.Tag = new PropertyProxy { Container = obj };
});
public class PropertyProxy
{
DependencyObject Container;
public SomeType TestProperty { get { return GetTestProperty(Container); } }
}
}
Now you can use the sub-property of Tag in your SortDescription:
<SortDescription PropertyName="Tag.TestProperty" />
If there is only a single property to be sorted, you can simply use the Tag for it.
The main problem with this is that using the Tag property will conflict with any other code that also tries to use the Tag. So you may want to look for some obscure DependencyProperty in the standard libraries that doesn't even apply to the objects in question and use that instead of Tag.
I am using the DataForm for an entity with about 40 attributes. I'm happy with how the form displays all but 3 of the attributes. These 3 attributes happen to be lists of items.
I don't want to have to code out an entire edit template, seems very counter productive.
<dataFormToolkit:DataForm AutoGenerateFields="True" CurrentItem="{Binding XXX, Mode=TwoWay, Source={StaticResource XXXViewModel}}" >
<dataFormToolkit:DataField Label="Client" >
<ListBox ItemsSource="{Binding Client}"></ListBox>
</dataFormToolkit:DataField>
</dataFormToolkit:DataForm>
The the WCF RIA Services includes a Silverlight Business Application project template that demonstrates creating a CustomDataForm where they override OnAutoGeneratingField and modify the field for just the attributes you want. I've copied the code here for you to illustrate the idea but I'd suggest you check out the real thing to see how they are using the ReplaceTextBox extension method to deal with the Data Binding as well. Download link.
public class CustomDataForm : DataForm
{
protected override void OnAutoGeneratingField(DataFormAutoGeneratingFieldEventArgs e)
{
// Get metadata about the property being defined
PropertyInfo propertyInfo = this.CurrentItem.GetType().GetProperty(e.PropertyName);
// Do the password field replacement if that is the case
if (e.Field.Content is TextBox && this.IsPasswordProperty(propertyInfo))
{
e.Field.ReplaceTextBox(new PasswordBox(), PasswordBox.PasswordProperty);
}
// Keep this newly generated field accessible through the Fields property
this.fields[e.PropertyName] = e.Field;
// Call base implementation (which will call other event listeners)
base.OnAutoGeneratingField(e);
}
}
It will work : try that
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class IsPassword : System.Attribute { }
public class CustomDataForm : DataForm
{
protected override void OnAutoGeneratingField(DataFormAutoGeneratingFieldEventArgs e)
{
// Get metadata about the property being defined
PropertyInfo propertyInfo = this.CurrentItem.GetType().GetProperty(e.PropertyName);
// Do the password field replacement if that is the case
var attributes = propertyInfo.GetCustomAttributes(typeof(IsPassword), false).ToList();
if (attributes.Any(obj=>obj is IsPassword))
{
PasswordBox box= new PasswordBox();
Binding binding = new Binding(e.PropertyName);
binding.Mode = BindingMode.TwoWay;
box.SetBinding(PasswordBox.PasswordProperty, binding);
e.Field.Content=box;
}
base.OnAutoGeneratingField(e);
}
}
then just add [IsPassword] to your property
I'm pretty sure it's not possible. If I were you I would swallow my grief and create that edit template.
The only alternative I can see is to work with the data in your viewmodel and create a separate class that holds the 37 properties that need no changing. Then you make a separate entity for the 3 that need special attention. This way you could have two data forms, one autogenerated and one custom. Hopefully you can then work with styling them so they look like one form. A lot of work, I know, but it might be even more work to create the full edit template.
Is it possible to introduce 'custom' attributes into different UI Elements in XAML ? Also to read them later like we add attributes for server controls in ASP.NET ?
I intend to read specific attributes and operate on them together.
It sounds like you're trying to find Attached Properties.
An attached property lets you add in a property, definable in Xaml, which can be "attached" to any UIelement. You then retrieve them in code like any other Dependency Property.
Here is the approach I tend to take with this.
Create a new class file called Meta:-
namespace SilverlightApplication1
{
public static class Meta
{
#region SomeValue
public static string GetSomeValue(DependencyObject obj)
{
return (string)obj.GetValue(SomeValueProperty);
}
public static void SetSomeValue(DependencyObject obj, string value)
{
obj.SetValue(SomeValueProperty, value);
}
public static readonly DependencyProperty SomeValueProperty =
DependencyProperty.RegisterAttached("SomeValue", typeof(string), typeof(Meta),
new PropertyMetadata(null));
#end region
#region SomeOtherValue
// Boilerplate code from above.
#end region
}
}
A value can now be attached in XAML like this:-
<TextBox x:Name="txt" local:Meta.SomeValue="Hello, World!" />
At some point in code this value can be retrieved with:-
string value = Meta.GetSomeValue(txt);
Note you don't have to stick with String as the type of the property you can pretty much use any type you like with the limitation that if you can to attach it in XAML the type must be compatible with the way XAML constructs objects (for example requires a default constructor).
The way I've accomplished that is by creating a new class that inherits the base control.
For example, I have a class called WebTextBox that inherits TextBox. And inside WebTextBox are some custom properties and events. By doing this you're inheriting all the behaviors of the TextBox control. But you can get creative here if you choose, even modifying the behavior by overriding events and such.
Anyway, after you create the class you'll then have to add the namespace for the project to the XAML. Something like this:
xmlns:me="clr-namespace:YourNamespace;assembly=YourAssembly"
And then you can add a WebTextBox (or whatever you call it) like this:
<me:WebTextBox CustomAttribute="cool stuff" />
I'm creating a UserControl and I just can't remember the name of the attribute which you use to decorate the property which you want to act as the default content property.
To give a concrete example, say i have a property called 'Title' which i can set using property syntax like this -
<local:myControl Title="the title"/>
But the consumer of the control may want to use element syntax like this -
<local:myControl> the Title </local:myControl>
I KNOW there is an attribute which I need to add to the Title property with to enable this support but I've forgotten what it is and can't find it anywhere.
Could anyone refresh my memory for me? Also, I'm looking for a similar attribute to act on CustomControls inheriting from ItemsControl.
ContentPropertyAttribute
I also found the code for supporting collections as the content property on MSDN. TOM_C is to thank for this.
[ContentProperty("SomeObjects")]
public class SomeContainer
{
private List<SomeObject> _someObjects;
public List<SomeObject> SomeObjects
{
get
{
if (null == _someObjects)
_someObjects = new List<SomeObject>();
return _someObjects;
}
}
}
XAML:
<SomeContainer>
<SomeObject/>
<SomeObject/>
<SomeObject/>
</SomeContainer>