Add Content to UserControl - wpf

I have a UserControl named myControl and there is a 3 columns Grid within it.
<Grid Name="main">
<Grid Grid.Column="0"/><Grid Grid.Column="1"/><Grid Grid.Column="2"/>
</Grid>
The client can use it in this way and it is ok.
<myControl />
My problem is, the client want to add an Element into the first column of "main" Grid, like:
<myControl>
<TextBlock Text="abc"/>
</myControl>
In this case, the TextBlock will replace the originated Content, here it is the "main" Grid.
What should I do to support the additional element? Great thanks.

You can use something like following:
// This allows "UserContent" property to be set when no property is specified
// Example: <UserControl1><TextBlock>Some Text</TextBlock></UserControl1>
// TextBlock goes into "UserContent"
[ContentProperty("UserContent")]
public partial class UserControl1 : UserControl
{
// Stores default content
private Object defaultContent;
// Used to store content supplied by user
private Object _userContent;
public Object UserContent
{
get { return _userContent; }
set
{
_userContent = value;
UpdateUserContent();
}
}
private void UpdateUserContent()
{
// If defaultContent is not set, backup the default content into it
// (will be set the very first time this method is called)
if (defaultContent == null)
{
defaultContent = Content;
}
// If there is something in UserContent, set it to Content
if (UserContent != null)
{
Content = UserContent;
}
else // Otherwise load default content back
{
Content = defaultContent;
}
}
public UserControl1()
{
InitializeComponent();
}
}

Related

TabControl region, How to pass parameter to child region? WPF - Prism

I'm using prism regions in order to create dynamic TabControl. But I'm having a problem passing the object from TabItem (parent view) to its child regions.
The below is the code I'm using to build the TabControl.
Shell:
xaml
<ContentControl regions:RegionManager.RegionName="ShellProjectRegion" />
ShellViewModel
regionManager.RegisterViewWithRegion(ShellProjectRegion, typeof(ProjectTabView));
ProjectTabView:
xaml
<TabControl regions:RegionManager.RegionName="ProjectTabRegion">
ProjectTabViewModel
container.RegisterType<object, ProjectView>(typeof(ProjectView).FullName);
ProjectView:
xaml
<Grid>
<ContentControl regions:RegionManager.RegionName="ProjectExplorerRegion"
regions:RegionManager.RegionContext="{Binding}" />
</Grid>
ProjectViewModel
public class ProjectViewModel : BindableBase, INavigationAware, IActiveAware {
private ProjectItem _project;
public ProjectItem Project {
get { return _project; }
set { SetProperty(ref _project, value); }
}
public ProjectViewModel(IRegionManager regionManager) {
regionManager.RegisterViewWithRegion("ProjectExplorerRegion", typeof(ProjectExplorerView));
}
public void OnNavigatedTo(NavigationContext navigationContext) {
Project = (ProjectItem)navigationContext.Parameters["project"];
}
}
ProjectExplorerView:
xaml.cs
public ProjectExplorerView(IUnityContainer container) {
InitializeComponent();
var vm = container.Resolve<ProjectExplorerViewModel>();
RegionContext.GetObservableContext(this).PropertyChanged += (s, e) => {
var context = (ObservableObject<object>)s;
var projectVm = (ProjectViewModel)context.Value;
vm.ParentProjectInfo = projectVm.Project.ProjectInfo;
};
DataContext = vm;
}
Note: Please note that in the last piece of code inside the ProjectExplorerView.xaml.cs the view constructor gets called multiple times each time new Tab is created. when tracing the code, the context variable gets null sometimes, and sometimes has the right value, which is the project I want to pass. but the it's always null at the end of calling the constructor.
So I'm not sure if this is the right way to do it, but it works.
First I've removed regionManager.RegisterViewWithRegion("ProjectExplorerRegion", typeof(ProjectExplorerView)); from ProjectViewModel to ShellViewModel, this was causing the view to be called multiple times as I have mentioned at the end of my question.
Second update the ParentProjectInfo implementation to use INotifyPropertyChanged, and inside the property setter, update what needs to be automatically updated.

Redefine ContentControl.Content in WPF

I'm trying to create a dialog class in WPF. This class inherits from Window and provides some default buttons and settings.
The implementation basically looks like this:
namespace Commons {
public class Dialog : Window {
public new UIElement Content {
get { return this.m_mainContent.Child; }
set { this.m_mainContent.Child = value; }
}
// The dialog's content goes into this element.
private readonly Decorator m_mainContent = new Decorator();
// Some other controls beside "m_mainContent".
private readonly StackPanel m_buttonPanel = new StackPanel();
public Dialog() {
DockPanel content = new DockPanel();
DockPanel.SetDock(this.m_buttonPanel, Dock.Bottom);
content.Children.Add(this.m_buttonPanel);
content.Children.Add(this.m_mainContent);
base.Content = content;
}
public void AddButton(Button button) {
...
}
}
}
As you can see, I redefined the Content property.
Now I want to be able to use this dialog class in XAML like this:
<my:Dialog x:Class="MyDialogTest.TestDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Commons;assembly=Commons"
Title="Outline" Height="800" Width="800">
<!-- Dialog contents here -->
</my:Dialog>
However, this will use Window.Content when setting the dialog's contents rather than Dialog.Content. How do I make this work?
You might need to indicate a property in "your" class as being the "content property" so that the child elements described by the XAML "content" of your Dialog get put into it instead of in the "content" property of your base Window.
[ContentProperty("Content")]
public class Dialog : Window {
If that doesn't work, then try changing the name.....so try this:
[ContentProperty("DialogContent")]
public class Dialog : Window {
public new UIElement DialogContent {
get { return this.m_mainContent.Child; }
set { this.m_mainContent.Child = value; }
}

Data bound ListBox won't update

I have created a class Track which represents a song in the playlist:
public class Track
{
public Uri Path
{
get { return path; }
set { path = value; }
}
public TrackState State
{
get { return state; }
set { state = value; }
}
private Uri path;
private TrackState state;
}
Next I have created MainWindowController class which interacts between the UI window and the Track class:
public class MainWindowController : INotifyPropertyChanged
{
public ObservableCollection<Track> Playlist
{
get { return playlist; }
set
{
if (value != this.playlist)
{
playlist = value;
NotifyPropertyChanged("Playlist");
}
}
}
public int NowPlayingTrackIndex
{
set
{
if (value >= 0)
{
playlist[nowPlayingTrackIndex].State = TrackState.Played;
playlist[value].State = TrackState.NowPlaying;
this.nowPlayingTrackIndex = value;
}
}
}
private ObservableCollection<Track> playlist;
private int nowPlayingTrackIndex;
}
Basically, this class stores playlist collection and an index of the currently played track. Lastly, I have created the UI window in WPF:
<Window ...>
...
<ListBox
Name="PlaylistListBox"
ItemsSource="{Binding Source={StaticResource ResourceKey=PlaylistViewSource}}"
ItemTemplateSelector="{Binding Source={StaticResource ResourceKey=TrackTemplateSelector}}"
MouseDoubleClick="PlaylistListBox_MouseDoubleClick" />
...
</Window>
and the corresponding code behind:
...
private void PlaylistListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
int index = this.PlaylistListBox.SelectedIndex;
this.windowController.NowPlayingTrackIndex = index;
}
...
Items source points to the static resource where the CollectionViewSource is defined. The ItemTemplateSelector defines which DataTemplate to use for list box items depending on the track state (NowPlaying or Played).
When the user double-clicks the playlist item, the NowPlayingTrackIndex in the MainWindowController gets updated and it updates the track state. The problem is, the DataTemplates for list box items do not get updated on the window, i.e. the the double-clicked list box item does not change the data template. Why?
I tried setting the PropertyChanged for track state but it didn't help. What am I missing? Thank you.
There are two issues to be addressed in your code.
First, you should know that ObservableCollection notify its observers about changes to its own elements, it doesn't know or care about changes to the properties of its elements. In other words, it doesn't watch for property change notification on the items within its collection. So changing the Track object property value in the PlayList collection doesn't watch by any mean. Here is an article about the subject.
Second, your MainWindowController doesn't broadcast for NowPlayingTrackIndex property value change at all. You should call NotifyPropertyChanged("NowPlayingTrackIndex") to notify interesting parties about the change of the current playing track. This may solve your problem but more elegant way, and my suggestion, would be implementing a custom ObservableCollection class (something like TrackObservableCollection) that contains NowPlaying property rather than implementing it in the MainWindowController class which looks like an unnecessary intermediation.

Binding a Textbox to a property in WPF

I have a Textbox in a User Control i'm trying to update from my main application but when I set the textbox.Text property it doesnt display the new value (even though textbos.Text contains the correct data). I am trying to bind my text box to a property to get around this but I dont know how, here is my code -
MainWindow.xaml.cs
outputPanel.Text = outputText;
OutputPanel.xaml
<TextBox x:Name="textbox"
AcceptsReturn="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Text="{Binding <!--?????--> }"/> <!-- I want to bind this to the Text Propert in OutputPanel.xmal.cs -->
OutputPanel.xaml.cs
namespace Controls
{
public partial class OutputPanel : UserControl
{
private string text;
public TextBox Textbox
{
get {return textbox;}
}
public string Text
{
get { return text; }
set { text = value; }
}
public OutputPanel()
{
InitializeComponent();
Text = "test";
textbox.Text = Text;
}
}
}
You have to set a DataContext in some parent of the TextBox, for example:
<UserControl Name="panel" DataContext="{Binding ElementName=panel}">...
Then the binding will be:
Text="{Binding Text}"
And you shouldn't need this - referring to specific elements from code behind is usually bad practice:
public TextBox Textbox
{
get {return textbox;}
}
I hope this example will help you.
1) Create UserControl.
2) Add to XAML <TextBlock Text="{Binding Path=DataContext.HeaderText}"></TextBlock>
3) In the code behind of that UserControl add
public partial class MyUserControl: UserControl
{
public string HeaderText { set; get; } // Add this line
public MyUserControl()
{
InitializeComponent();
DataContext = this; // And add this line
}
}
4) Outside of the control and let's say in the MainWindow Load event you have to do like
this.gdMain = new MyUserControl{ HeaderText = "YES" };
If your are starting to bind properties I suggest you check some articles on MVVM.
This is a very powerful architecture you can use on WPF. I found it very useful in my projects.
Check this one.

Setting and displaying a property in a WPF UserControl

Am i missing something here? I have created a usercontrol with a property and for arguments sake it has a text box in it.
<UserControl x:Class="Isd.Utility.SystemMonitorWpf.Bar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock x:Name="txtExpected" Grid.Column="0" Grid.ColumnSpan="2" Width="auto" Height="auto" FontSize="10" LayoutTransform="{StaticResource Rotate}" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Tahoma" Foreground="Red" Panel.ZIndex="100" Margin="5,5,5,5"/>
Then in the code behind i have
public partial class Bar : UserControl
{
private string _PropTest;
public string PropTest
{
get { return _PropTest; }
set { _PropTest = value; }
}
public Bar()
{
InitializeComponent();
txtExpected.Text = PropTest;
}
}
Then i drop the usercontrol into the xaml and set the property to a value
<local:Bar PropTest="test"></local:Bar>
In this example, when the usercontrol is displayed the text is showing as null, its like the property PropTest never got set. Am I missing something obvious here? Thanks in advance.
When used as an attribute like you have it, PropTest gets set after the constructor is called, so it doesn't get set when you apply the property to the text box.
You'd be better attaching an event to the property changing, or use the TextBox as the backing value for the property.
It's because the value of the attribute will never set on the Text-Property of the txtExpected-Control. At the time when the constructor is called, the property PropTest still null.
So, you have to change the implementation of your property:
public string PropTest
{
get { return txtExpected.Text; }
set { txtExptected.Text = value; }
}
You should use DependencyProperties, so you can bind your control properties via xaml
On your class declaration:
public static readonly DependencyProperty MyProperty = DependencyProperty.Register(
"MyProperty", //Property name
typeof(string), //Property type
typeof(MyControl), //Type of the dependency property provider
new PropertyMetadata(MyPropertyChanged));//Callback invoked on property value has changes
public string MyProperty
{
set
{
this.SetValue(MyProperty, value);
}
get
{
return (string)this.GetValue(MyProperty);
}
}
private static void MyPropertyChanged( object sender, DependencyPropertyChangedEventArgs args )
{
// update your control inner elements properties
}
Edited a few times because of typos :P
You don't appear to be doing anything in the setter of PropTest. It won't be set prior to construction, so it will be null when you do:
txtExpected.Text = PropTest;
Inside your constructor. If you do this:
public string PropTest
{
get { return _PropTest; }
set
{
_PropTest = value;
txtExpected.Text = PropTest;
}
}
It should work. It's not what I'd call an "ideal" way to do things though, you might want to take a look at Dependency Properties, INotifyPropertyChanged and Binding.
What happens when you add Text attribute like so:
<TextBlock x:Name="txtExpected" Text="{Binding PropTest}" />
and eliminate the line
txtExpected.Text = PropTest;
from the constructor?
Delegate value assignment in PropTest property to TextBox:
public string PropTest
{
get { return txtExpected.Text; }
set { txtExpected.Text = value; }
}

Resources