How to access array element by x:Static in XAML? [duplicate] - wpf

This question already has answers here:
Binding to static property
(12 answers)
Closed 3 months ago.
I had try the following, but got syntax error.
class A {
public static int[] Numbers { get; } = new[] { 1, 2, 3 };
}
<TextBlock Text="{x:Static A.Numbers[0]}" /> <!-- syntax error -->

"x:Static" only supports accessing members of the type.
See the documentation: StaticExtension Class.
To use a path (and the index is part of the path) you need a binding.
An example of such a binding:
<TextBlock Text="{Binding [0], Source={x:Static lib:A.Numbers}}" />
Where lib: is the prefix associated with the namespace in which class A resides.
Or use a more modern binding syntax (since WPF 4.5):
<TextBlock Text="{Binding (lib:A.Numbers)[0]}" />
P.S. Also, to eliminate possible errors, you should change the implementation of the Numbers property - protect the collection from change or make it observable.
Example:
public class A
{
public static ReadOnlyCollection<int> Numbers { get; } = Array.AsReadOnly(new int[] { 1, 2, 3 });
// Or
public static ObservableCollection<int> Numbers { get; } = new ObservableCollection<int> () { 1, 2, 3 };
}

Related

Silverlight 5 ,Prism 5.1 StaticResource Use Not working of static class

I am unable to understand a thing which is as follows. My project is in Prism 4.1 Sliverlight 5. I'm Using MVVM pattern.
I've a static class like this
{
public static class RegionNames
{
public static string AUTH_LOGIN_REGION = "AuthRegion";
public static string TAB_TEST_REGION = "TabRegion";
public static string USER_TAB_REGION="UserTabRegion";
}
}
I tried to use this class in Shell.xmal like below.
<Grid.Resources>
<inf:RegionNames x:Key="rName"></inf:RegionNames>
</Grid.Resources>
Now this Resource I used in textblock
Result :No text appeared.
<TextBlock Text="{Binding Source={StaticResource rNamee}, Path=USER_TAB_REGION}" Margin="20"></TextBlock>
Now I changed this class like below:
{
public class RegionNames : INotifyPropertyChanged
{
public static string AUTH_LOGIN_REGION = "AuthRegion";
public static string TAB_TEST_REGION = "TabRegion";
public static string USER_TAB_REGION="UserTabRegion";
public RegionNames() {
AuthReginName = "HOLY COW POW POW !!";
}
private string _authReginName;
public string AuthReginName {
get {
return _authReginName;
}
set {
_authReginName = value;
OnPropertyChanged("AuthReginName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
And used it like this
<TextBlock Text="{Binding Source={StaticResource rNamee}, Path=AuthReginName}"></TextBlock>
Result : Text Appeared
this time it worked. Why? My static defied string values are not coming?.
Is there any relation with Object creation of class & Setting Properties values?
First of all for instance objects you can bind only with properties, that's why second solution works for you. (You can't bind with fields)
And for static properties you can bind with fields but you need to use x:Static markup extension. (same goes for properties as well)
<TextBlock Text="{x:Static inf:RegionNames.USER_TAB_REGION}" Margin="20"/>
My Second Question with this thread About x:Static
For Silverlight 5 who want to Use x:Static in xaml.
http://brianlagunas.com/creating-a-silverlight-5-static-markup-extension/
is Helpful & Worked for me.

ComboBox does not select proper item when bound to CompositeCollection

I have a ComboBox bound to a collection of animals. From it I select my favourite animal. I need a static null item above the bound items. I declare it using a CompositeCollection. When the ComboBox is bound it does not select my initial favourite animal. How can I fix that? Similar problem here but still unresolved.
Observations:
Binding to the static item works i.e. if I don't have an initial favourite animal the static item gets selected.
The problem disappears if the static item is removed. Of course this would make the CompositeCollection and this whole question obsolete.
I already applied these measures:
A CollectionContainer cannot bind directly to a property as outlined here.
The composite collection is also moved to a static resource as suggested here.
Complete C# code and XAML to demonstrate the problem:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public class Animal
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Zoo
{
private IEnumerable<Animal> _animals = new Animal[]
{
new Animal() { Id = 1, Name = "Tom" },
new Animal() { Id = 2, Name = "Jerry" }
};
public Zoo(int initialId)
{
FavouriteId = initialId;
}
public int FavouriteId { get; set; }
public IEnumerable<Animal> Animals { get { return _animals; } }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void BindComboBox(object sender, RoutedEventArgs e)
{
// Selecting the static item by default works.
//DataContext = new Zoo(-1);
// Selecting "Jerry" by default does not work.
DataContext = new Zoo(2);
}
}
}
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<Window.Resources>
<CollectionViewSource x:Key="AnimalsBridge" Source="{Binding Path=Animals}" />
<CompositeCollection x:Key="AnimalsWithNullItem">
<local:Animal Id="-1" Name="Pick someone..."/>
<CollectionContainer Collection="{Binding Source={StaticResource AnimalsBridge}}" />
</CompositeCollection>
</Window.Resources>
<StackPanel>
<Button Content="Bind" Click="BindComboBox"/>
<ComboBox x:Name="cmbFavourite"
SelectedValue="{Binding Path=FavouriteId}"
SelectedValuePath="Id" DisplayMemberPath="Name"
ItemsSource="{StaticResource AnimalsWithNullItem}"/>
</StackPanel>
</Window>
I dont have a solution to your problem but rather an alternative. I personally have view models dedicated to each view. I would then have a property on the view model to add the null value as required. I prever this method since it allows for better unit testing of my viewmodel.
For your example add:
public class ZooViewModel
{
.....
public IEnumerable<Animal> Animals { get { return _animals; } }
public IEnumerable<Animal> AnimalsWithNull { get { return _animals.WithDefault(new Animal() { Id = -1, Name = "Please select one" }); } }
}
The magic component
public static class EnumerableExtend {
public static IEnumerable<T> WithDefault<T>(this IEnumerable<T> enumerable,T defaultValue) {
yield return defaultValue;
foreach (var value in enumerable) {
yield return value;
}
}
}
Then in your XAML you just bind to
ComboBox x:Name="cmbFavourite"
SelectedValue="{Binding Path=FavouriteId}"
SelectedValuePath="Id" DisplayMemberPath="Name"
ItemsSource="{Binding AnimalsWithNull }"/>
Now you are binding directly to the source and can control the binding as normal. Also note because we are using "yield" we are not creating a new enum but rather just iterating over the existing list.

Change Binding at runtime and save class instance WPF

I am writing a WPF program write a program on .NET 4.5 which will hold a lot of settings inside and I am faced with several problems.
For example, I have a camera and I need to create another instance of that camera settings at runtime. For XAML page I have a lot of bindings and now for the second instance I need to clear them an use bindings for new instance of that class in which I hold properties for that settings (If I am thinking correctly, of course) So, I have 2 questions:
How do I change my binding so that I can write the minimum amount of code possible (please, keep in mind that I don't know how many instances will be created)?
How I can create second, third, etc. instances of a class and to lose objects in memory because I need to hold every instance of each class during runtime and just change bindings while switching between these instances.
Create a view model that manages and exposes the settings for you. Use an additional property to provide the currently selected settings:
public class CameraSettings
{
public string Title { get; set; }
public bool Grayscale { get; set; }
}
public class CameraViewModel : INotifyPropertyChanged
{
private CameraSettings _SelectedSettings;
private List<CameraSettings> _Settings;
public event PropertyChangedEventHandler PropertyChanged;
public IEnumerable<CameraSettings> Settings
{
get { return _Settings; }
}
public CameraSettings SelectedSettings
{
get { return _SelectedSettings; }
set
{
if (_SelectedSettings != value)
{
_SelectedSettings = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedSettings"));
}
}
}
}
public CameraViewModel()
{
_Settings = new List<CameraSettings>()
{
{ new CameraSettings() { Title = "BlackWhite", Grayscale = true } },
{ new CameraSettings() { Title = "TrueColor", Grayscale = false } }
};
}
}
Then you can bind your view to this view model. Example view:
<Window.DataContext>
<local:CameraViewModel />
</Window.DataContext>
<StackPanel>
<ComboBox ItemsSource="{Binding Settings}" SelectedItem="{Binding SelectedSettings, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Title}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding SelectedSettings.Grayscale}" />
</StackPanel>

Binding to Complex Objects in the ViewModel from the View?

Say for example I have the following type:
public class Site
{
public string Name { get; set; }
public int SiteId { get; set; }
public bool IsLocal { get; set; }
}
The above type can be assigned to be held in a Propety in a ViewModel like so assuming a corresponding backing field has been created but omitted here ofc:
public Site SelectedSite
{
get { return _selectedSite; }
set
{
_selectedSite = value;
// raise property changed etc
}
}
In my xaml a straight forward binding would be:
<TextBlock x:Name="StatusMessageTextBlock"
Width="Auto"
Height="Auto"
Style="{StaticResource StatusMessageboxTextStyle}"
Text="{Binding MessageToDisplay,
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
Can you extend a binding by using the dot notation syntax? e.g:
<TextBlock x:Name="StatusMessageTextBlock"
Width="Auto"
Height="Auto"
Style="{StaticResource StatusMessageboxTextStyle}"
**Text="{Binding SelectedSite.Name,**
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
Seems like a an interesting feature but my gut instinct is a no as my DC is being assigned at RunTime so at DesignTime or CompileTime, I can't see any clues that could make this feature work or not?
Correct me if have misunderstood what a complex object is, I have simplified mine down for the sake of this question.
Of course this is possible. However, WPF needs to know when any property along the path has changed. To that end, you need to implement INotifyPropertyChanged (or other supported mechanisms). In your example, both Site and the VM containing SelectedSite should implement change notification).
Here's how you could implement the functionality you specified in your question:
// simple DTO
public class Site
{
public string Name { get; set; }
public int SiteId { get; set; }
public bool IsLocal { get; set; }
}
// base class for view models
public abstract class ViewModel
{
// see http://kentb.blogspot.co.uk/2009/04/mvvm-infrastructure-viewmodel.html for an example
}
public class SiteViewModel : ViewModel
{
private readonly Site site;
public SiteViewModel(Site site)
{
this.site = site;
}
// this is what your view binds to
public string Name
{
get { return this.site.Name; }
set
{
if (this.site.Name != value)
{
this.site.Name = value;
this.OnPropertyChanged(() => this.Name);
}
}
}
// other properties
}
public class SitesViewModel : ViewModel
{
private readonly ICollection<SiteViewModel> sites;
private SiteViewModel selectedSite;
public SitesViewModel()
{
this.sites = ...;
}
public ICollection<SiteViewModel> Sites
{
get { return this.sites; }
}
public SiteViewModel SelectedSite
{
get { return this.selectedSite; }
set
{
if (this.selectedSite != value)
{
this.selectedSite = value;
this.OnPropertyChanged(() => this.SelectedSite);
}
}
}
}
And your view might look something like this (assuming a DataContext of type SitesViewModel):
<ListBox ItemsSource="{Binding Sites}" SelectedItem="{Binding SelectedSite}"/>
Below is what worked for me:
public Site SelectedSite
{
get { return _selectedSite; }
set
{
_selectedSite = value;
RaisePropertyChanged("SelectedSite");
}
}
In my xaml I was able to do:
<TextBox Name="tbSiteName"
Width="250"
Height="30"
Margin="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsReadOnly="True"
Style="{StaticResource MainTextBoxStyle}"
Text="{Binding SelectedSite.Name,
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
This allows you to access data members off the Site Type without having to create individual properties that wrap each data member on the Site Type. Then individual controls can bind to each property declared in the VM. In a one to one fashion, this aproach can become rather verbose. The binding extension attached to the Text property of the TextBox control shown above, shows that we are not binding to a simple straight forward property but actually to a custom type. Potentially removing the need to create more public properties.

Silverlight databinding error

I found an example online that explains how to perform databinding to a ListBox control using LINQ in WPF. The example works fine but when I replicate the same code in Silverlight it doesn't work. Is there a fundamental difference between Silverlight and WPF that I'm not aware of?
Here is an Example of the XAML:
<ListBox x:Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" FontSize="18"/>
<TextBlock Text="{Binding Role}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is an example of my code behind:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
string[] names = new string[] { "Captain Avatar", "Derek Wildstar", "Queen Starsha" };
string[] roles = new string[] { "Hero", "Captain", "Queen of Iscandar" };
listBox1.ItemSource = from n in names from r in roles select new { Name = n, Role = r}
}
Silverlight does not support binding to anonymous types. (To be technically correct, Silverlight does not support reflecting against internal types, and since anonymous types are internal, this doesn't work). See this article for a simple workaround- you will merely need to create a model class to hold the data.
public class MyItem
{
public string Name { get; set; }
public string Role { get; set; }
}
listBox1.ItemSource = from n in names from r in roles select new MyItem() { Name = n, Role = r}
In Silverlight you can not bind to anonymous types. Silverlight requires the type of the item being bound to be public but anonymous types are by internal.
You will need to to create a public type to carry your results:-
public class MyItem
{
public string Name {get; set; }
public string Role {get; set; }
}
now in your code:-
listBox1.ItemSource = from n in names from r in roles select new MyItem() { Name = n, Role = r}

Resources