Get WPF TabItem container from UserControl - wpf

I am setting the content of a UserControl, SIASystemTab, to a TabItem, ConfigTab. How do you access the TabItem from the UserControl using code behind? I would like to reuse the methods in the TabItem from multiple UserControls.
var subSystem = new SIASystemTab(opCo);
var configTab = new ConfigTab()
{
Header = "New Header*",
Content = subSystem
};

One simple way is to create a property in SIASystemTab like this
public ConfigTab myConfigTab {get; set;}
Then assign the ConfigTab instance to this property
subSystem.myConfigTab = configTab;
Later on u can use this as per your need

Cast the Content property of the UserControl to ConfigTab:
var tab = this.Content as ConfigTab;
var siaSystemTab = tab.Content;

Related

How to add WPF Behavior in the code behind

I am trying to find a way to add behavior in the code, I am able to add it successfully in XAML.
This is how I am adding the behavior in XAML to a grid, SelectedItems is a DP in the behavior and it is data bind to the view model selected items property. I am listening to the grid collection changed event and updating the DP which in turns notify the view mode of the selected items
/// <summary>
/// Dependency Property SelectedItems
/// </summary>
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems",
typeof(INotifyCollectionChanged), typeof(MultiSelectBehavior),
new PropertyMetadata(null));
AssociatedObject.SelectedItems.CollectionChanged += GridSelectedItems_CollectionChanged;
<i:Interaction.Behaviors>
<behaviors:MultiSelectBehavior SelectedItems="{Binding SelectedItems}"/>
</i:Interaction.Behaviors>
What I need is to create this behavior in the code behind. I am doing this in the constructor of the window that contains the grid, but it is not working, the viewmodel selected items property is not getting updated.
var multiSelectBehavior = new MultiSelectBehaviorSingleton();
BindingOperations.SetBinding(this.BackupsGrid, MultiSelectBehavior.SelectedItemsProperty,
new Binding()
{
Source = this.DataContext,
Path = new PropertyPath("SelectedItems"),
Mode = BindingMode.OneWay
});
Interaction.GetBehaviors(this.BackupsGrid).Add(multiSelectBehavior);
Try this:
var multiSelectBehavior = new MultiSelectBehavior();
BindingOperations.SetBinding(multiSelectBehavior, MultiSelectBehavior.SelectedItemsProperty, new Binding("SelectedItems"));
Interaction.GetBehaviors(this.BackupsGrid).Add(multiSelectBehavior);
The accepted answer does not appear to work in the designer, because the OnAttached event is never raised. An approach that works at runtime and also in the designer is using the Attach() method on the behavior. In this case, that would look like this:
var multiSelectBehavior = new MultiSelectBehavior();
BindingOperations.SetBinding(multiSelectBehavior, MultiSelectBehavior.SelectedItemsProperty, new Binding("SelectedItems"));
multiSelectBehavior.Attach(this.BackupsGrid)

Activation/deactivation of toolbar buttons using Prism

I’m in the process of learning the Prism framework and I’ve come along way already. But I was wondering about how to create toolbars (and context menus) where each module can register their own buttons.
For this example I want all buttons to reside in the same ToolBar control which is located in my Shell. The ToolBars ItemsSource binds to a ToolBarItems property of type ObservableCollection<FrameworkElement> in the view model. Elements can be added to this collection using a ToolBarRegistry service. This is the ViewModel:
public class ShellViewModel
{
private IToolBarRegistry _toolBarRegistry;
private ObservableCollection<FrameworkElement> _toolBarItems;
public ShellViewModel()
{
_toolBarItems = new ObservableCollection<FrameworkElement>();
_toolBarRegistry = new ToolBarRegistry(this);
}
public ObservableCollection<FrameworkElement> ToolBarItems
{
get { return _toolBarItems; }
}
}
Note that the collection of type FrameworkElement will be refactored to be of a more concrete type if this turns out to be the correct solution.
My ToolBarRegistry has a method to register image buttons:
public void RegisterImageButton(string imageSource, ICommand command)
{
var icon = new BitmapImage(new Uri(imageSource));
var img = new Image();
img.Source = icon;
img.Width = 16;
var btn = new Button();
btn.Content = img;
btn.Command = command;
_shellViewModel.ToolBarItems.Add(btn);
}
I call this method from my OrderModule and the buttons show up correctly. So far so good.
The problem is how I can control when these buttons should be removed again. If I navigate to a view in another module (and sometimes another view in the same module), I want these module-specific buttons to be hidden again.
Do you have any suggestions on how to do this? Am I approaching this problem the wrong way, or can I modify what I already have? How did you solve this problem?
I would not insert Button instances in the ObservableCollection. Think about this approach instead:
Create ViewModel for the toolbar buttons
class ToolBarButtonViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation to be provided by you
public string ImageSource { get; set; }
public ICommand Command { get; set; }
public bool IsVisible { get; set; }
}
Then of course change the type of ToolBarItems to a collection of these.
In your ShellView, add a DataTemplate for ToolBarButtonViewModel and bind the ItemsSource of whatever your toolbar control is to the collection of ViewModels, for example:
<DataTemplate>
<Button Command="{Binding Command}">
<Button.Content>
<Image Source="{Binding ImageSource}" />
</Button.Content>
</Button>
</DataTemplate>
You can now bind Button.Visibility to IsVisible with a BooleanToVisibilityConverter to solve your immediate problem.
As an added bonus, you can also:
Change the visual appearance of the toolbar buttons entirely from XAML
Bind any property of the visual tree for a toolbar button to corresponding properties on the ToolBarButtonViewModel
Update
The mechanism for enabling/disabling buttons depends on specifics of your application. There are many options -- here are a few (keep this chart in mind while reading):
Implement INavigationAware in your Views or ViewModels and enable/disable buttons as required
Attach handlers to the events of IRegionNavigationService of the region(s) of interest and have the handlers enable or disable buttons
Route all navigation through your own code (CustomNavigationService) and decide what to do inside it

WPF: Data binding with code

How do I use data-binding from code (C# or VB)?
This is what I have so far, but it is displaying Binding.ToString instead of m_Rep.FirstName.
Public ReadOnly Property TabCaption As Object
Get
Return New Label With {.Foreground = Brushes.Black, .Content = New Binding("FirstName"), .DataContext = m_Rep}
End Get
End Property
Yes, binding in code is a little different from straight assignment (which is how XAML makes it look like it works).
I can give you an example in C# - shouldn't be too far removed from VB.NET.
var label = new Label { Foreground = Brushes.Black, DataContext = m_Rep };
label.SetBinding(Label.ContentProperty, new Binding("FirstName"));
return label;
So the "SetBinding" method binds the "FirstName" path (of the DataContext) to the label's Content property.
You should use m_Rep as a Source of Binding
I have some sample C# code for you as below
Person myDataSource = new Person("Joe");
// Name is a property which you want to bind
Binding myBinding = new Binding("Name");
myBinding.Source = myDataSource;
// myText is an instance of TextBlock
myText.SetBinding(TextBlock.TextProperty, myBinding);
Hope to help

How does XAML set readonly CLR properties?

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);

How do I load user controls dynamically?

How can I load a user control[s] in a window dynamically (using code at runtime)?
I'd highly recommend having a look at Prism, since composite user interfaces is what it's for. However, since this would require you refactoring your entire application, I'll also answer your question directly.
If you want a single user control in a container, put a ContentControl in your XAML and then set the Content property. If you are using a view model, you could bind Content to a FrameworkElement property on the view model:
contentControlInstance.Content = new CustomUserControl();
If you want multiple controls in a list, use an ItemsControl and assign an ObservableCollection<> to the ItemsSource property. If you are using a view model, you could bind ItemsSource to an ObservableCollection property on the View Model.
Then you can just add/remove views from that ObservableCollection:
private ObservableCollection<FrameworkElement> views =
new ObservableCollection<FrameworkElement>();
private void Initialize()
{
itemsControl.ItemsSource = views;
}
private void AddView(FrameworkElement frameworkElement)
{
views.Add(frameworkElement);
}
For adding multiple controls you need container.
Suppose you have a StackPanel container "myStack"
<Window ..>
<StackPanel Name="MyStack" />
</Window>
You can create control dynamically and add it to container. See code below
void AddButtons()
{
Button B1=new Button(),B2=new Button(), B3=new Button();
B1.Content="Hello";
B2.Content="First";
B3.content="Application";
// Now you can set more properties like height, width, margin etc...
MyStack.Children.Add(B1);
MyStack.Children.Add(B2);
MyStack.Children.Add(B2);
}
Or use binding. Here's a really crude example showing how different WPF controls can be shown in a single WPF window using ContentControl and binding (which is what a toolkit like Prism or Caliburn Micro does).
XAML:
<UserControl x:Class="ViewA">
...
<UserControl/>
<UserControl x:Class="ViewB">
...
<UserControl/>
Code:
void ShowViewModelDialog (object viewModel)
{
var host = new MyViewHost();
FrameworkElement control = null;
string viewModelName = viewModel.GetType().Name;
switch (viewModelName )
{
case ("ViewModelA"):
control = new ViewA();
break;
case ("ViewModelB"):
control = new ViewB();
break;
default:
control = new TextBlock {Text = String.Format ("No view for {0}", viewModelName);
break;
}
if (control!=null) control.DataContext = viewModel;
host.DataContext = control;
host.Show(); // Host window will show either ViewA, ViewB, or TextBlock.
}

Resources