Is it possible to store a reference to a Page in a Custom Control so that when the control is clicked that page loads (like a custom menu item)?
My code so far:
public class ccMenuItem : Button
{
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(Control));
public static readonly DependencyProperty BackColorProperty = DependencyProperty.Register("BackColor", typeof(SolidColorBrush), typeof(Control), new UIPropertyMetadata(Brushes.White));
public static readonly DependencyProperty PageProperty = DependencyProperty.Register("Page", typeof(Page), typeof(Control));
public Page Page
{
get { return (Page)GetValue(PageProperty); }
set { SetValue(PageProperty, value); }
}
public string Title
{
get { return GetValue(TitleProperty).ToString(); }
set { SetValue(TitleProperty, value); }
}
static ccMenuItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ccMenuItem), new FrameworkPropertyMetadata(typeof(ccMenuItem)));
}
}
The code compiles OK, however how do I assing a page (say the class is called vpn) to the Page property in XAML?
If your "application" is using a NavigationWindow instead of Window, then you can get to the NavigationService and tell it to change the page.
protected override void OnClick()
{
NavigationService ns = NavigationService.GetNavigationService(this);
ns.Navigate( Page );
}
Related
I develop a wpf application on which I interact with a huge grid of buttons (built with an ItemsControl).
When I click on one button, the app displays a new window. This new window displays an UserControl.
I use a service to show the new window :
public class WindowService
{
#region Variable
#endregion Variable
#region Constructor
public WindowService()
{
}
#endregion Constructor
#region Properties
#endregion Properties
#region Public Method
public void ShowWindow(object viewModel)
{
var win = new WindowView();
//win.Content = viewModel; <-- not the best way, go to see in comment why (thanks #Ndubuisi Jr)
win.DataContext = viewModel;
win.Show();
}
#endregion Public Method
#region Private Method
#endregion Private Method
}
And the code to call this method :
public void display_InfoPoste(object commandParameter)
{
windowPoste = new WindowService();
windowPoste.ShowWindowCommandParameter(new InfoPosteViewModel(commandParameter));
}
No Problem with that. (The window displayed is only a content to receive different UserControl)
Now, I have a button "close" on the user control, but I don't find any way to close the window.
I work with MVVM pattern, that's why I don't find yet how to do that.
Could you help me?
(I can share a screenshot with you if you need)
Thanks a lot
Picture : Part of the project's arborescence
Just below, the requested code of the "InfoPosteViewModel.cs"
#region Variable
private string _commandParameter;
#endregion Variable
#region Constructor
public InfoPosteViewModel()
{
//FermerCommand = new RelayCommand(Action_FermerWindow);
}
public InfoPosteViewModel(object commandParameter)
{
SelectedViewModel = new InfoPosteViewModel();
_commandParameter = (string)commandParameter;
ID = _commandParameter;
}
#endregion Constructor
#region Properties
public ICommand FermerCommand { get; set; }
private static string _id;
public string ID
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("ID");
}
}
private object _selectedViewModel;
public object SelectedViewModel
{
get
{
return _selectedViewModel;
}
set
{
_selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
#endregion Properties
#region Public Method
public void Action_FermerWindow(object commandParameter)
{
}
#endregion Public Method
Try passing the close command to the VM that is being displayed in the Dialog. Then link that to the close button or action. Because it's the close command of the VM that opened the dialog you can then close the dialog using the reference in that VM and continue with any clean up or follow up code you need.
Oh, and you'll need to make the window an instance variable rather than a local variable.
The cleanest way to close a Window from its ViewModel in MVVM is using an attached property.
public static class perWindowHelper
{
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached(
"CloseWindow",
typeof(bool?),
typeof(perWindowHelper),
new PropertyMetadata(null, OnCloseWindowChanged));
private static void OnCloseWindowChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
if (!(target is Window view))
return;
if (view.IsModal())
view.DialogResult = args.NewValue as bool?;
else
view.Close();
}
public static void SetCloseWindow(Window target, bool? value)
{
target.SetValue(CloseWindowProperty, value);
}
public static bool IsModal(this Window window)
{
var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
return fieldInfo != null && (bool)fieldInfo.GetValue(window);
}
}
This can be used in the View's xaml file
<Window
....
vhelp:perWindowHelper.CloseWindow="{Binding ViewClosed}" />
bound against the ViewClosed property (of type bool?) from the ViewModel. Setting this property value will close the View.
More details on my blog post
I am facing an issue with my code below where the call back is getting called only the first time DocText is set. Subsequently setting the DocText is not calling my callback. I am trying to reset the html of the browser by setting the same string to the DocText property.
WebBrowser class
public static readonly DependencyProperty DocumentTextProperty =
DependencyProperty.Register("DocumentText", typeof(string),
typeof(WebBrowser), new
FrameworkPropertyMetadata(string.Empty,TextChangedCallback));
private static void TextChangedCallback(DependencyObject
dependencyObject, DependencyPropertyChangedEventArgs
dependencyPropertyChangedEventArgs)
{
var control = (WebBrowser)dependencyObject;
control._browser.DocumentText = dependencyPropertyChangedEventArgs.NewValue.ToString();
}
public string DocumentText
{
get { return (string)GetValue(DocumentTextProperty); }
set { SetValue(DocumentTextProperty, value); }
}
Factory class that creates the instance of the web browser is using the browsers set binding to bind the dependency property to "DocText"
if (!String.IsNullOrEmpty(documentTextProperty))
{
browser.SetBinding(WebBrowser.DocumentTextProperty, documentTextProperty);
}
And the documentTextProperyt is being set like this from the ViewModel class
DocText = "some html string";
public string DocText
{
get
{
_docText = if(html != null? html: string.empty);
return _docText;
}
set
{
docText = value;
OnPropertyChanged(() = DocText);
}
}
Looks like the Notification is not going back to control(WebBrowser). Please check whether INotifyPropertyChanged is properly implemented. I checked equivalent code in my system and it is properly working.
public class MaskedTextBox : TextBox
{
public static readonly DependencyProperty DocumentTextProperty =
DependencyProperty.Register("DocumentText", typeof(string),
typeof(MaskedTextBox), new PropertyMetadata(default(string), TextChangedCallback));
private static void TextChangedCallback(DependencyObject
dependencyObject, DependencyPropertyChangedEventArgs
dependencyPropertyChangedEventArgs)
{
var control = (MaskedTextBox)dependencyObject;
control.Text= dependencyPropertyChangedEventArgs.NewValue.ToString();
}
public string DocumentText
{
get { return (string)GetValue(DocumentTextProperty); }
set { SetValue(DocumentTextProperty, value); }
}
}
<StackPanel Margin="50,5,5,50">
<local:MaskedTextBox x:Name="text1" DocumentText="{Binding ElementName=text2, Path=Text, Mode=OneWay}"/>
<TextBox x:Name="text2"/>
</StackPanel>
Here if you edit second text box it is updating first one by calling the callback method properly!
we have been working a complete day on this problem and have it all summed up to a small example. We are currently converting a project from Silverlight to WPF, in Silverlight both versions work, in WPF only one does.
We have a simple control with a string-type dependencyproperty like this:
public class MyControl : Control
{
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(MyControl), new PropertyMetadata(null, TextChanged));
private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
Then we have a class with an attached property as follows:
public class MyAttachedProperty
{
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.RegisterAttached("Description", typeof(String), typeof(MyAttachedProperty), new PropertyMetadata(null, DescriptionPropertyChanged));
public static String GetDescription(DependencyObject obj, String value)
{
return (String)obj.GetValue(DescriptionProperty);
}
public static void SetDescription(DependencyObject obj, String value)
{
obj.SetValue(DescriptionProperty, value);
}
private static void DescriptionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var MySuperbControl = d as MyControl;
Debug.WriteLine("The control's text is: " + MySuperbControl.Text);
}
public static void DoNothing()
{
}
}
We implement our control like this in MainWindow.xaml:
<ContentControl x:Name="MyContentControl">
<ContentControl.ContentTemplate>
<DataTemplate>
<local:MyControl x:Name="MyCntrl" Text="DefaultText" att:MyAttachedProperty.Description="Test"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
and in the code-behind have this constructor:
public MainWindow()
{
MyAttachedProperty.DoNothing();
InitializeComponent();
}
If you start the project this way, the Debug-text will not contain any text. If you call DoNothing() after InitializeComponent(), it will show the text. Can anyone please explain, why? Note, in Silverlight both ways work. Also, if you do not use the control in a datatemplate both ways work.
It's interesting side effect. It makes sense when you think that DependencyProperty registration adds it to some global collection. If you call static constructor on MyAttachedProperty first it is added to the collection first and set first for an object.
If you force static constructor to run first on MyControl by adding same empty static method DoNothing then you can do
public MainWindow()
{
MyControl.DoNothing();
MyAttachedProperty.DoNothing();
InitializeComponent();
}
and the text will be shown
or in case
public MainWindow()
{
MyAttachedProperty.DoNothing();
MyControl.DoNothing();
InitializeComponent();
}
the empty text will be shown.
I have a User Control that I'm using to control the content of all the pages I have in a project. It allows me to easily control the header and footers of the page and have matching styles etc etc. I designed this all in Visual Studio 2010 and used the XAML Designer built in to layout all my pages. I used to be able to click on objects on the design surface that were inside the MainContent dependency property and have it highlight the XAML definition of that control. After I upgraded to VS2012, something broke and now it will only highlight flxv:MasterPageCustomControl when I click on any of the content inside my MainContent Grid. I've tried to fix it several times and lived with it for over a year now... can anyone help me? I'm not sure how much more information to provide.
<flxv:MasterPageCustomControl HeaderTitle="Display Menu"
HeaderDescription="Modify the properties and content of this display"
HeaderIcon="/SFD.Client;component/Images/32x32/plasma-tv.png"
FooterContentTemplate="{StaticResource FooterContentTemplate}">
<flxv:MasterPageCustomControl.MainContent>
<Grid>
<Button/>
</Grid>
</flxv:MasterPageCustomControl.MainContent>
</flxv:MasterPageCustomControl>
User Control Code:
public partial class MasterPageCustomControl : System.Windows.Controls.UserControl
{
#region Fields
public static readonly DependencyProperty FooterContentTemplateProperty;
public static readonly DependencyProperty HeaderDescriptionProperty;
public static readonly DependencyProperty HeaderIconProperty;
public static readonly DependencyProperty HeaderTitleProperty;
public static readonly DependencyProperty MainContentProperty;
#endregion Fields
#region Properties
public DataTemplate FooterContentTemplate
{
get { return (DataTemplate)GetValue(FooterContentTemplateProperty); }
set { SetValue(FooterContentTemplateProperty, value); }
}
public string HeaderDescription
{
get { return (string)GetValue(HeaderDescriptionProperty); }
set { SetValue(HeaderDescriptionProperty, value); }
}
public string HeaderIcon
{
get { return (string)GetValue(HeaderIconProperty); }
set { SetValue(HeaderIconProperty, value); }
}
public string HeaderTitle
{
get { return (string)GetValue(HeaderTitleProperty); }
set { SetValue(HeaderTitleProperty, value); }
}
public object MainContent
{
get { return (object)GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
#endregion Properties
#region Constructors
static MasterPageCustomControl()
{
FooterContentTemplateProperty = DependencyProperty.Register(
"FooterContentTemplate",
typeof(DataTemplate),
typeof(MasterPageCustomControl));
HeaderDescriptionProperty = DependencyProperty.Register(
"HeaderDescription",
typeof(string),
typeof(MasterPageCustomControl));
HeaderIconProperty = DependencyProperty.Register(
"HeaderIcon",
typeof(string),
typeof(MasterPageCustomControl));
HeaderTitleProperty = DependencyProperty.Register(
"HeaderTitle",
typeof(string),
typeof(MasterPageCustomControl));
HeaderWorkstationVisiblity = DependencyProperty.Register(
"HeaderWorkstationVisiblity",
typeof(Visibility),
typeof(MasterPageCustomControl),
new PropertyMetadata(Visibility.Visible));
MainContentProperty = DependencyProperty.Register(
"MainContent",
typeof(object),
typeof(MasterPageCustomControl));
// ---------------------------------------
DefaultStyleKeyProperty.OverrideMetadata(typeof(MasterPageCustomControl), new FrameworkPropertyMetadata(typeof(MasterPageCustomControl)));
}
Sorry to be cliche... but I'm pretty new to WPF and MVVM so I'm not sure how to handle this properly. I have a WinForms control within one of my views that I need to modify in it's code behind when an event is raised in the ViewModel. My view's datacontext is inherited so the viewmodel is not defined in the views constructor. How would I go about properly handling this? I am not using any frameworks with built in messengers or aggregators. My relevant code is below. I need to fire the ChangeUrl method from my ViewModel.
EDIT: Based on the suggestion from HighCore, I have updated my code. I am still not able to execute the ChangeUrl method however, the event is being raised in my ViewModel. What modifications need to be made??
UserControl.xaml
<UserControl ...>
<WindowsFormsHost>
<vlc:AxVLCPlugin2 x:Name="VlcPlayerObject" />
</WindowsFormsHost>
</UserControl>
UserControl.cs
public partial class VlcPlayer : UserControl
{
public VlcPlayer()
{
InitializeComponent();
}
public string VlcUrl
{
get { return (string)GetValue(VlcUrlProperty); }
set
{
ChangeVlcUrl(value);
SetValue(VlcUrlProperty, value);
}
}
public static readonly DependencyProperty VlcUrlProperty =
DependencyProperty.Register("VlcUrl", typeof(string), typeof(VlcPlayer), new PropertyMetadata(null));
private void ChangeVlcUrl(string newUrl)
{
//do stuff here
}
}
view.xaml
<wuc:VlcPlayer VlcUrl="{Binding Path=ScreenVlcUrl}" />
ViewModel
private string screenVlcUrl;
public string ScreenVlcUrl
{
get { return screenVlcUrl; }
set
{
screenVlcUrl = value;
RaisePropertyChangedEvent("ScreenVlcUrl");
}
}
WPF does not execute your property setter when you Bind the property, instead you must define a Callback method in the DependencyProperty declaration:
public string VlcUrl
{
get { return (string)GetValue(VlcUrlProperty); }
set { SetValue(VlcUrlProperty, value); }
}
public static readonly DependencyProperty VlcUrlProperty =
DependencyProperty.Register("VlcUrl", typeof(string), typeof(VlcPlayer), new PropertyMetadata(null, OnVlcUrlChanged));
private static void OnVlcUrlChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var player = obj as VlcPlayer;
if (obj == null)
return;
obj.ChangeVlcUrl(e.NewValue);
}
private void ChangeVlcUrl(string newUrl)
{
//do stuff here
}