Is there a way to logically group controls in WPF - wpf

Here's the scenario
I have a Grid with some TextBlock controls, each in a separate cell in the grid. Logically I want to be able to set the Visibility on them bound to a property in my ViewModel. But since they're each in a separate cell in the grid, I have to set each TextBlock's Visibility property.
Is there a way of having a non-visual group on which I can set common properties of its children? Or am I dreaming?

There is no non-visual group that would make this possible.
Setting the Visibility properties, directly or in a common Style shared by all of the TextBlocks, is probably the simplest solution.

Another option is to bind the visibility property of each item in your group of items to one single item, that way in your code behind you are only ever having to set the visibility of one item.

If possible I mostly place them in a GroupBox and set the groupbox BorderThickness to 0. That way all controls are grouped, you don't see that it's a groupbox and you can set the visibility with one property..
<Style TargetType="{x:Type GroupBox}"
x:Key="HiddenGroupBox">
<Setter Property="BorderThickness"
Value="0" />

I hope you have defined all of your cell UI elements inside a DataTemplate. You can do a small trick at the ViewModel level to achieve what you are looking for.
Have Singleton class at the ViewModel, which should have the Visibility or an equivalent property which you wanted to bind to every TextBlock.
The Singleton class should implement INotifypropertyChanged to get the change notification to the UI
Bind the Singleton property in the XAML and control this property from anywhere in your application.
< TextBlock Visibility="{Binding Source={x:Static local:Singleton.Instance},Path=Visibility}"
And a simple Singleton class can be implemented as
public class Singleton :INotifyPropertyChanged
{
private Singleton() { }
public static Singleton Instance
{
get
{
if (instance == null){ instance = new Singleton(); }
return instance;
}
}
private Visibility _visibility;
public Visibility Visibility
{
get { return _visibility; }
set
{
_visibility = value;
PropertyChanged( this, new PropertyChangedEventArgs("Visibility") );
}
}
public event PropertyChangedEventHandler PropertyChanged;
private static Singleton instance;
}
Now you can control Singleton.Instance.Visibility = Visibility.Collapsed anywhere from your code behind

It may be possible to make a custom control that redirects all its add/remove children methods to its own parent, while still keeping a record of its contents so it can apply its own property styles. Would be tricky though.

I realise that this a very ancient question, but there will no doubt people finding this thread after searching for something related. Therefore I offer the following very simple solution:
Place all of the controls in question into a new grid that sits within the existing grid; spans the appropriate cells and replicates them within it's own structure. Then you can change the visibility of the new grid, and with it the controls inside.

Related

How do I make user controls for both ListView and ListViewItem work with each other?

I have all the styling, triggers, etc. down for ListView and ListViewItem, and I want to turn them into user controls. How do I make sure that these two "match up" with each other, so that MyListView accepts MyListViewItems as content? Also, considering that I must end the ListView tag by the end of the user control XAML file, I am not sure how I would add items to it.
If you want them to be reusable with different data sets, especially through binding, you should stay away from UserControls and just make custom controls derived from the original types. In that case you create a standalone MyListView.cs and MyListViewItem.cs and all of the XAML for the controls goes into default Styles (usually also containing a ControlTemplate) in Themes/Generic.xaml. You can see an example of this setup by just adding a WPF Custom Control to your WPF project from Add New Item.
Once you've created the .cs files for your custom controls you just need to override a few methods from the base ItemsControl to use MyListViewItem as the item container control. The ListView would end up like this:
public class MyListView : ListView
{
static MyListView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyListView), new FrameworkPropertyMetadata(typeof(MyListView)));
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MyListViewItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is MyListViewItem;
}
}
You can now use your custom MyListView exactly as you would a normal ListView, including binding to ItemsSource.
Inheritance should take care of that for you. In other words, if you have two user controls, the first one with a basic element of ListView (not UserControl) and the other of ListViewItem (again, not UserControl), and you make sure they extend ListView and ListViewItem respectively in the .cs code, the following should work equally:
ListView lv = new ListView();
lv.Items.Add(new ListViewItem());
or
MyListView mlv = new MyListView();
mlv.Items.Add(new myListViewItem()); //If your myListView extends ListView, and myListViewItem extends ListViewItem in your user control files, of course
In case you are looking for a XAML solution, you should import your namespace at the top
xmlns:myControls="WhateverYourNamespaceAndAssemblyAre"
and on you page/window/whatever
<myControls:myListView>
<myControls:myListViewItem/>
<myControls:myListViewItem/>
</myControls:myListView>

WPF and data binding

I have a WPF app where i want to control IsEnabled property of several textboxes in code by setting just one bool. So i decide to add databinding for textboxes IsEnabled property.
Here is the source object definition:
<Window.Resources>
<src:QuestionControlsState x:Key="QuestionContorlsState" IsEnabled="True"/>
</Window.Resources>
Where 'QuestionControlsState' simple class with only one public property 'IsEnabled'
Then i bind some of textboxes:
<TextBox Name="textBoxQuestion"
IsEnabled="{Binding Path=IsEnabled, Source={StaticResource QuestionContorlsState}}">
At this point it works fine, when i change IsEnabled attribute in Window.Resources section databinding works.
But i want to control it from code, so i get source object:
QuestionControlsState _questionControlsState = (QuestionControlsState)this.FindResource("QuestionContorlsState");
And now when i try to set _questionControlsState.IsEnabled, textbox state not change and there is now warnings in output.
Without seeing your code, I'm guessing your QuestionControlsState class isn't implementing INotifyPropertyChanged.
Modify it like this:
public class QuestionControlsState : INotifyPropertyChanged
{
private bool isEnabled = true;
public bool IsEnabled
{
get { return isEnabled; }
set
{
isEnabled = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsEnabled"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
This will trigger a PropertyChanged event whenever you change the IsEnabled property, notifying the view it needs to refresh.
Of course, if you're using the MVVM pattern, the correct way of doing this is binding all textboxes to a boolean IsEnabled property in the ViewModel and not by trying to find the static resource in codebehind... Then, a simple IsEnabled = false in the VM will disable all textboxes (without the need of a staticresource)
Please use the MVVM pattern to passing data to tha XAML view and to encapsulate the view logic and to make the view logic testable.
With MVVM its very easy to create a observable property which can be bound to the IsEnabled properties of your controls. You only have to change the Property with a Command to true or false to enable or disable the property.
Thank you guys, Blachshma you was right i forgot to implement INotifyPropertyChanged interface on my custom class and now it works like it should. Thank you! I think about MVVM pattern and it looks cool but i just started with WPF and want to learn basics.
You can try to change StaticResource to DynamicResource.
You can find information here

DataContext for binding a UserControl with ViewModel within DataTemplate

What I'm trying to achieve is:
Have a ListView bound to an ObservableCollection of ItemRecords.
Have a TabControl that contains detailed view for all the ItemRecords in the ListView that were selected for editing.
Each TabItem contains a UserControl ("ItemInfo") that uses ItemInfoViewModel as its VM (and, not so coincidentally, DataContext).
ItemInfo UserControl needs to be populated with the data from the corresponding ItemRecord.
To achieve that, I'm trying to pass the ItemRecord (selected in the ListView) to ItemInfoViewModel.
Finally, the question: what do you think would be the best way to do this, without breaking the MVVM pattern?
The not-so-elegant way that I see (and it actually doesn't exactly follow the MVVM principles) is to have a DependencyProperty ItemRecord defined in the UserControl, provide its value via binding, and in the constructor (in the UserControl's code-behind) pass the ItemRecord to the VM (which we get by casting the DataContext).
The other problem is with how to actually pass the ItemRecord via binding.
Once I set the VM as the UserControl's DataContext, I cannot just use {Binding} to specify the current item in TabControl's source collection.
At the moment I am binding to the TabControl's SelectedItem using ElementName - but this doesn't sound too robust :-(
<localControls:TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer>
<localControls:ItemInfo ItemRecord="{Binding ElementName=Tabs, Path=SelectedItem}"/>
</ScrollViewer>
</DataTemplate>
</localControls:TabControl.ContentTemplate>
Any good advice will be greatly appreciated!
Alex
I think your problem is you're not quite understanding the MVVM pattern here; you're still looking at this as the different controls talking to each other. Where in MVVM, they should not be, each control is communicating with the view model independently of all the others. And the view model controls (and supplies) the logic which tells the controls how to behave.
So, ideally you would have something like:
public ObservableCollection<ItemRecord> ListViewRecords
{
get { ... }
set { ... }
}
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set { ... }
}
The ListViewRecords would be bound to the ItemsSource property of your ListView (the actual properties might vary based on the specific controls you're using, I'm used to the Telerik suite at the moment so that's where my head is). And the SelectedListViewRecords would be bound to the SelectedItems property of the ListView. Then for your TabControl you would have:
public ObservableCollection<MyTabItem> Tabs
{
get { ... }
set { ... }
}
public MyTabItem SelectedTab
{
get { ... }
set { ... }
}
Again, you would bind the Items property to the Tabs and SelectedItem to the SelectedTab on your TabControl. Now your view model contains all the logic, so in your SelectedListViewRecords you might do something like this:
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set
{
_selectedRecords = value;
NotifyPropertyChanged("SelectedListViewRecords");
Tabs.Clear(); // Clear the existing tabs
// Create a new tab for each newly selected record
foreach(ItemRecord record in value)
Tabs.Add(new MyTabItem(record));
}
}
So the idea here is that the controls do nothing more than send and receive property changes, they know nothing of the underlying data, logic, etc. They simply show what their bound properties tell them to show.

WPF - Overriding IsVisible on my custom control

I have a custom control (an Adorner) that wraps another control (a textbox).
public class MyCustomAdorner : Decorator
{
...
public MyCustomAdorner()
{
Child = new TextBox();
}
}
I want to be able to override the VisibilityChanged so that the MyCustomAdorner's event is only fired if the Child's visiblity changes, not the actual decorator. How would I go about this?
On a first approach I would try to bind the Adorner's Visibility to the TextBox's Visibility (not sure if this one works). This way, if the textbox changes visibility, the adorner will follow. If you bind them two way, then it will work the other way around too. So if you don't want it to work both ways, make sure you don't set the Adorner's Visibility.
If binding the Visibilities together doesn't work, you can try to declare a new property (for example a bool), that manages the visibilities, and bind the two Visibilities to that bool through a Converter. And when you want to change something around the Visibilities, you set this bool to a new value.
Overriding the VisibilityChange event doesn't sound good to me, also I'm not sure if you can even do that...

WPF: DependencyProperty of custom control fails when using several instances of the control

I've built a custom control in WPF that inherits from ListBox. In this I have implementet my own property that is a BindingList. To make this property bindable I've implemeneted it as a DependencyProperty:
public BindingList<CheckableListItem> CheckedItems
{
get
{
return (BindingList<CheckableListItem>)GetValue(MultiComboBox.CheckedItemsProperty);
}
set
{
SetValue(MultiComboBox.CheckedItemsProperty, value);
}
}
public static readonly DependencyProperty CheckedItemsProperty;
I register this DependencyProperty in a static constructor inside my custom control:
CheckedItemsProperty = DependencyProperty.Register("CheckedItems",
typeof(BindingList<CheckableListItem>),
typeof(MultiComboBox),
new FrameworkPropertyMetadata(new BindingList<CheckableListItem>()));
(MultiComboBox is the name of my custom control. CheckableListItem is a simple class I've written just for this purpose).
This BindingList is then updated inside the custom control (never outside) as the user interacts with it.
When I use my custom control in XAML I bind to the CheckItems property with the mode "OneWayToSource". I'm using the MVVM pattern and the property in the ViewModel that I'm binding to is also a BindingList. The ViewModel never affects this list, it just reacts at the changes that the custom control make to the list. The property in the ViewModel looks like this:
private BindingList<CheckableListItem> _selectedItems;
public BindingList<CheckableListItem> SelectedItems
{
get
{
return _selectedItems;
}
set
{
if (value != _selectedItems)
{
if (_selectedItems != null)
{
_selectedItems.ListChanged -= SelectedItemsChanged;
}
_selectedItems = value;
if (_selectedItems != null)
{
_selectedItems.ListChanged += SelectedItemsChanged;
}
OnPropertyChanged("SelectedItems");
}
}
}
As you can see I'm listening to changes made to the list (these changes always occur inside my custom control), and in the "SelectedItemsChanged"-method I update my Model accordingly.
Now...this works great when I have one of these controls in my View. However, if I put two (or more) of them in the same View strange things start to happen. This will of course mean that I'll have two lists with selected items in my ViewModel. But if do something in the View that changes one of the lists, both lists are affected! That is, the event handlers for the event ListChanged is triggered for both list if changes are made to any one of them!
Does anyone recognize this problem and/or have a solution to it? What is wrong with my implementation?
My first though is that the DependencyProperty is static. Normally that means shared between all instances. But I guess DependencyProperties work in some other "magical" way so that might not be the problem.
Any tips or hints is appreciated!
I had a similar problem with a collection-type dependency property. My solution was taken from the MSDN article on Collection-Type Dependency Properties. It was adding the following line
SetValue(OperatorsPropertyKey, new List<ListBoxItem>()); //replace key and type
in the constructor of my control because it seems that a collection-type dependency property constructor is being called only once no matter how many instances your control containing this collection has (static eh).
This sounds like you bound both/all the Views to the same ViewModel. That would explain that changes to one cause changes in the other.

Resources