Silverlight: Using DataContextProxy to access ObserableCollection elements - silverlight

I'm trying to get binding working in the header of a grid column which does not have access to the DataContext. To give it access, I used the DataContextProxy described here: http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
This is a simplified version of my ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private String _myString;
private ObservableCollection<TabItemViewModel> _tabItems;
public String MyString { blah... }
public ObservableCollection<TabItemViewModel> TabItems {blah... }
}
and it works for accessing the MyString using XAML like this:
<TextBlock Text="{Binding Source={StaticResource DataContextProxy}, Path=DataSource.MyString}"/>
but I'm not sure how to get it to point at the ErrorHeading inside the observable collection of TabItemViewModels...
public class TabItemViewModel : INotifyPropertyChanged
{
private string _errorHeading;
public string ErrorHeading
{
get { return _errorHeading; }
set
{
_errorHeading = value;
RaisePropertyChanged("ErrorHeading");
}
}
}
I tried it like this:
<TextBlock Text="{Binding Source={StaticResource DataContextProxy}, Path=DataSource.TabItems.ErrorHeading}"/>
but I dont think you can dig into the ObservableCollection like this - I'm not even sure how it knows which element in the collection to look at.

In your TabItemViewModel implementation, you've defined ErrorHeading two times.
At one place you write
RaisePropertyChanged("ErrorHeading");
while another place you've written
OnPropertyChanged("ErrorHeading");
Looks like there is some serious problem with your code. On the top of it, you've not implemented the interface INotifyPropertyChanged in your TabItemViewModel.
So fix these problems first. Maybe, then you would be able to do something more elegant in your code. :-)

Related

How to bind command in ViewModel to a command in Behavior?

WPF project + Prism 7 + (Pure MVVM pattern)
Simple, I have TextBox which need to be cleared when some button is pressed (without the violation to the MVVM pattern)
<Button Command="{Binding ClearCommand}"/>
<TextBox Text="{Binding File}">
<i:Interaction.Behaviors>
<local:ClearTextBehavior ClearTextCommand="{Binding ClearCommand, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</TextBox>
ViewModel
public class ViewModel {
public ICommand ClearCommand { get; set; }
}
Behavior
public class ClearTextBehavior : Behavior<TextBox>
{
public ICommand ClearTextCommand
{
get { return (ICommand)GetValue(ClearTextCommandProperty); }
set
{
SetValue(ClearTextCommandProperty, value);
RaisePropertyChanged();
}
}
public static readonly DependencyProperty ClearTextCommandProperty =
DependencyProperty.Register(nameof(ClearTextCommand), typeof(ICommand), typeof(ClearTextBehavior));
public ClearTextBehavior()
{
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
}
private void ClearTextCommandExecuted()
{
this.AssociatedObject.Clear();
}
}
The problem is the command in the ViewModel is always null (it did not bound to the command in the Behavior), Although I made sure that it is initialized in the behavior class.
NOTE: please do NOT suggest to set the File property to empty string, because this is just an example, In my real case, I need to select all the Text, so I really need an access to the AssociatedObject of the behavior
If i understood your Question correctly, you want to know why the ICommand in the ViewModel is not set to the DelegateCommand defined in the Behaviour.
The Problem is, that the ICommand and the DelegateCommand do not have a direct connection. I assume you may misunderstood how a Binding works and what happens by using those.
First of all, the ICommand is 'comes' from a Class and is therefore a reference Type.
Second, the reference to the ICommand is saved within the DependencyProperty ClearTextCommandProperty.
Third, by using a Binding in the XAML something like this happens as C# code:
Binding binding = new Binding();
binding.Path = new PropertyPath("ClearTextCommand");
binding.Source = ClearCommand;
BindingOperations.SetBinding(TextBox.ClearTextCommandProperty, binding);
Now the important thing: I don't know exactly which assignment comes first, but both lines will override the Value reference in the ClearTextCommandProperty!
//either here
SetBinding(TextBox.ClearTextCommandProperty, binding);
//or here
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
//Which could be written as
SetValue(ClearTextCommandProperty, new DelegateCommand(ClearTextCommandExecuted));
At no point there is an assignment like this:
ViewModel.ClearCommand = SomeICommand;
Therefore it is Null, as #Andy mentioned
Edited to match select all Text
Additionally, i suggest you drop this complex stuff and use the full potential of the Interactivity Package like this:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<utils:SelectAllText TargetName="TextToSelect"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<TextBox x:Name="TextToSelect" Text="{Binding File}"/>
And the SelectAllText
public class SelectAllText : TargetedTriggerAction<TextBox>
{
protected override void Invoke(object parameter)
{
if (Target == null) return;
Target.SelectAll();
Target.Focus();
}
}
If you take a look at this sample here:
https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx
You will notice that I have an ICommand there and it's set up to run a method.
If it was just an ICommand with a Get and Set like you have there then it would be NULL. There's a property but it's null until it is set to something.
This a very clunky way to implement an ICommand but relies on no external libraries or anything.
If you take a look at the second article in that series, it uses mvvmlight and relaycommand so creating a command is rather less clunky.
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx
public RelayCommand AddListCommand { get; set; }
public MainWindowViewModel()
{
AddListCommand = new RelayCommand(AddList);
}
private void AddList()
{
stringList.Add(myString));
}
If you look at that code AddListCommand is initially null.
It is set in the constructor to a new RelayCommand which means it is then not null.
This is fairly simple but the code for the command is in a different place to the property so a more elegant approach is usual. As shown here: https://msdn.microsoft.com/en-gb/magazine/dn237302.aspx
Having said all that.
Selecting all text is something to do in the view, not the viewmodel.
You shouldn't really be passing a piece of UI from the view into a viewmodel.
Rather than a command it could well be that you should be binding a bool which is set in the viewmodel and acted on in the behaviour.

Binding to properties of ObservableCollection-items

interface ICar
{
UserControl SmallView{ get; }
UserControl CompleteView{ get; }
}
class ViewModel
{
ObservableCollection<ICar> Cars{ get; set;}
ObservableCollection<UserControl> SmallViews{ get; }
ObservableCollection<UserControl> CompleteViews{ get; }
}
XAML
<ItemControl ItemsSource="{Binding SmallViews}"/>
<ItemControl ItemsSource="{Binding CompleteView}"/>
I am adding ICars instances to ViewModel.Cars collection. When that happens I want the two UserControls (small and Complete) to be added in the View (XAML).
-I can get it to work as I want, by setting the ItemsSources in CodeBehind when Cars.CollectionChanged is Raised. But I fear all the collection is redrawn for all items in ItemsSource.. I only want the changes to be added, and I would like en elegant solution without a lot of CodeBehind.
This Codebehind makes it work as intended - but I would like something cleaner somthing with real Binding.
CompleteControls and SmallControls are the names for the ItemControls above, which in this solution has no binding markup :-( .
public CarsView(ViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
UpdateViews(viewModel.Cars);
viewModel.Cars.CollectionChanged += (caller, args) =>
UpdateViews(args.NewItems.Cast<ICar>());
}
private void UpdateViews(IEnumerable<ICar> newCars)
{
foreach (var car in newCars)
{
CompleteControls.Items.Add(car.CompleteView);
SmallControls.Items.Add(car.SmallView);
}
}
I see a couple of flaws in your concepts.
class ViewModel
{
ObservableCollection<ICar> Cars{ get; set;}
// This is wrong for MVVM. You don't need this.
//
// ObservableCollection<UserControl> SmallViews{ get; }
// ObservableCollection<UserControl> CompleteViews{ get; }
}
It will also get rid of this.
UpdateViews(viewModel.Cars);
viewModel.Cars.CollectionChanged += (caller, args) =>
UpdateViews(args.NewItems.Cast<ICar>());
About the ObvservableCollection you need to know 2 things.
If you create it directly from another collection it will not trigger the event.
Your args.NewItems.Cast() will always produce one item only in a collection since the ObservableCollection does not have a AddRange() method.
I see that you came from Winforms. Start by inspecting more about MVVM. it will pay off very fast. You need to remember that if you are doing anything in the code behind with controls, stop doing it, cause your doing it wrong.
You will instantiate the UserControls in XAML.
<Listbox ItemsSource={Binding Cars}>
<Listbox.ItemsTemplate>
<DataTemplate>
<StackPanel>
<my:SmallViews />
<my:CompleteViews />
</StackPanel>
</DataTemplate>
</Listbox.ItemsTemplate>
</Listbox>
You should really avoid ever dealing with any UI elements in code behind

Bind Textbox Text to a Static Property

I have a Static Class with the following Static Property:
public static class PrintingMethods
{
public static String DocsCountString
{
get
{
return printDocuments.Count.ToString();
}
}}
I have a text box that I bind to this property:
<TextBlock Text="{x:Static my:PrintingMethods.DocsCountString}" x:Name="PagesNumber"/>
This works - I can see the number in the Text, But it never changes If the Property Value Change.
I am quite new to this, I know there are things like Dependency Object and INotify Interface but this won't work for Static.
If anyone can help me with a working code (modification to what I wrote) to Achieve real time textChange that would be great, Thanks!!!
Answer on our comments:
If you use the Singleton Pattern, you can bind to it like that
public sealed class MySingleton : INotifyPropertyChanged
{
public void RaiseProperty(string aPropName)
{
// implementation of INotifyPropertyChanged
}
public static MySingleton Instance
{
get{ return sInstance; }
}
public string MyProperty
{
get {return mMyProperty;}
set {mMyProperty = value; RaiseProperty("MyProperty"); }
}
private string mMyProperty;
private static MySingleton sInstance = new MySingleton();
}
As you can see you can easily use the INotifyPropertyChanged interface and implementation with a singleton class. You might want to make the constructor private to disallow creating another instance of this class. Also it would be possible to lazy allocate the MySingleton instance. You will find much more about singletons on stackoverflow.
<TextBlock Text="{Binding Source={x:Static my:MySingleton.Instance}, Path=MyProperty}"/>
The important part here now is the Binding and the overriden Source. Usually Binding takes the current DataContext. By setting a new Source the DataContext is irrelevant and the new Source is used to get the value behind the Path property.
you should use function in modifier is internal like:
Form2:
internal string foo()
{
return nom;
}
Form1:
form2 win= new form2();
win.ShowDialog();
Textbox.Text = win.foo();

Bind ListBox to another ListBox

I have 2 listBoxes, if you click an item in the top one, then the bottom one filters to a few results.
I am trying to learn WPF and MVVM and am wondering if this is the correct way to do this. Is this the best way?
Here is what I did:
class VisitInfoViewModel : ViewModelBase
{
List<ServiceType> serviceTypes;
List<ServiceType> allServiceTypes;
public VisitInfoViewModel()
{
ServiceCategories = ServiceCategory.Categories;
allServiceTypes = ServiceType.ServiceTypes;
}
public List<ServiceCategory> ServiceCategories { get; set; }
public List<ServiceType> ServiceTypes
{
get
{
return serviceTypes;
}
}
public ServiceCategory SelectedServiceCategory
{
get { return null; }
set
{
serviceTypes = allServiceTypes.FindAll(st => st.ServiceCategoryGuid.Equals(value.Guid));
OnPropertyChanged("ServiceTypes");
}
}
}
and MainWindow.xaml snippet
<ListBox ItemsSource="{Binding Path=VisitInfo.ServiceCategories}"
SelectedItem="{Binding Path=VisitInfo.SelectedServiceCategory}"
ItemTemplate="{StaticResource listBoxTemplate}"
Height="112"
HorizontalAlignment="Left"
Margin="6,30,0,0"
Name="lbxServiceCategory"
VerticalAlignment="Top"
Width="366" />
<ListBox ItemsSource="{Binding Path=VisitInfo.ServiceTypes}"
ItemTemplate="{StaticResource listBoxTemplate}"
HorizontalAlignment="Left"
Margin="6,0,0,19"
Name="lbxServiceType"
Width="366"
Height="121"
VerticalAlignment="Bottom" />
also, why shouldn't I just add an EventHandler for selectedItemChanged on my listBox?
It seems so much simpler and clearer to use the event handler.
I think it is because if I did that it would no longer by MVVM... is that correct?
What would you do and what are the best practices?
What you are doing is mostly fine - though I would personally make the SelectedServiceCategory a "real" property (with a value that's saved).
The difference with MVVM, and doing it in code behind, is that you're working with data. If you make the "Current Category" change the types, then you're working purely with the data, and not worrying about the UI at all. You can change the category by any mechanism, and the UI will always stay up to date.
I, personally, would suggest writing this more like so:
class VisitInfoViewModel : ViewModelBase
{
List<ServiceType> allServiceTypes;
public VisitInfoViewModel()
{
ServiceCategories = ServiceCategory.Categories;
allServiceTypes = ServiceType.ServiceTypes;
}
// This can use a private setter...
public IEnumerable<ServiceCategory> ServiceCategories { get; private set; }
private ServiceCategory currentCategory;
public ServiceCategory CurrentServiceCategory
{
get { return this.currentCategory; }
set
{
if (this.currentCategory != value)
{
this.currentCategory = value;
ServiceTypesInCurrentCategory = allServiceTypes.Where(st => st.ServiceCategoryGuid.Equals(this.currentCategory.Guid));
OnPropertyChagned("CurrentServiceCategory");
OnPropertyChanged("ServiceTypes");
}
}
}
public IEnumerable<ServiceType> ServiceTypesInCurrentCategory { get; private set; }
}
This provides complete freedom to change the CurrentServiceCategory in code or via Xaml, without any event handlers. It also makes your ViewModel completely data related - you don't know or care what is being used to display this - as long as you have something in your View that sets the CurrentServiceCategory, everything stays synchronized correctly.
also, why shouldn't I just add an EventHandler for selectedItemChanged on my listBox? It seems so much simpler and clearer to use the event handler. I think it is because if I did that it would no longer by MVVM... is that correct?
You can do that, but it's typically a violation of MVVM at this point. The main issue is that you'd be coupling the implementation to that event handler - by doing this, you're basically "locking in" the behavior based on your code in your View for this specific implementation of the View. By keeping it "pure" in terms of MVVM, you're View is free to change (ie: maybe you want to switch to a combobox for the ServiceCategories someday) without touching your ViewModel code at all...

Binding Enum[] to ListBox

I have next enumeration
Enum rcCategory
{
Incoming,
Internal,
Outgoing
}
and I have property "categories" in my class which has rcCategory[] type.
I would like to bind this property to the listBox. I use next code for this
MyListBox.SetBinding (ListBox.ItemsSource, new Binding {Source= myClass.categories});
But this code doesnt work as expected.
How Can I do this. My listBox always is empty but source property has value
See Bea Stollnitz top ranked article on it.
In short you need to bind to an ObjectProvider which calls the static method Enum.GetValues( typeof(YourEnum) ) to return the list.
http://bea.stollnitz.com/blog/?p=28
Update: Sorry got a slight speedreading issue. This one is easier.. Verified that it works. Recommended: Find up a copy of ProgrammingWPF and go thru the DataBinding chapter...
XAML:
<ListBox DockPanel.Dock="Left" ItemsSource="{Binding EnumArrayProp}"/>
Codebehind:
public partial class Window1 : Window
{
public rcCategory[] EnumArrayProp
{
get; set;
}
public Window1()
{
InitializeComponent();
this.EnumArrayProp = new rcCategory[] { rcCategory.Incoming, rcCategory.Incoming, rcCategory.Outgoing };
this.DataContext = this;
}

Resources