User control, custom control, inheritance and override - wpf

I know this question is probably going to get alot of "Duplicate question" comments but i have yet to see one with an answer that works or isn't simply "Avoid it at all cost". But here goes, i have created a control lets call it "ControlA" in a project called "ControlA_Project" and i was intending to override some of its virtual methods in a control called "ControlB" that inherites "ControlA" in another project called "ControlB_Project" . The idea being that "ControlA" has save and load methods that i wish to change from saving and loading to file, too saving and loading to database ( and if later on another save and load type is required i can just override those methods again ).
The problem i have is i originally had "ControlA" as a usercontrol and when i tried adding the new control with the overrides ("ControlB") into a window i would get this error:
The component 'ControlB_Project.ControlB' does not have a resource identified by the URI '/ControlA_Project;component/usercontrols/ControlA.xaml'.
Googling the error i came to the conclusion you could not inherit from a user control ( or for the sake of arguement it wasn't a good idea ). So i then changed the control from a user control to a custom control. this however then leads me to another problem, a template for a control doesnt link to the code behind (i.e On_Click) like it does in a user control and there is no easy way to simply override the behavier (as far as i am aware). I know i can retemplate ControlB by copy and pasting ControlAs template and changing a few lines but controlA has a large template as it is and making mutliple copies seems a waste of space.
So put simply is there a correct way to change the behavier of a control in wpf?

First, remember that the ControlTemplate can be changed by the user alot so you need to make sure that important fields are clearly marked.
So if you want an OnClick event. First mark your button as "important"
<Button x:Name="PART_MyButton"/>
Its also a good idea to mark this aswell on your control class
[TemplatePart(Name = "PART_MyButton", Type = typeof(Button))]
public class MyCustomControl : Control
Now you want to attach to the on click event of that button to do that, override the OnApplyTemplate method.
public override void OnApplyTemplate()
{
mButton = Template.FindName("PART_MyButton", this) as Button;
mButton.Click += MyEventHandler;
}
Depending on how well your control can work without the control, you should gracefully handle a not found control or throw an exception.
One final thing is. If you override a control which has a default style, it might be a good idea to provide a new default style.
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
}
and placing an implicit style in your generic.xaml

Related

How to add usercontrols from a static method (in winforms)?

I'm trying to create a function that adds usercontrols to my mainform. I want this function to be available from the usercontrols themselves as well, so I'm making it static.
My problem is that when it's static I can't use this.Controls.Add(ucontrol);, I tried to change it to ActiveForm.Controls.Add(ucontrol); but then ActiveForm is NULL and has no Controls property (throws an exception).
UPDATE #1: I ended up doing something like this inside the usercontrol:
MainForm ref_to_mainform_to_gain_access_to_functions = (MainForm)Parent;
ref_to_mainform_to_gain_access_to_functions.DisplayControl(ucontrol);
maybe it's even better, but I still assume it's possible to have access to the mainform from a static method?
UPDATE #2:
Application.OpenForms["FormName"]
worked from static method.
I still try to find out if that usercontrol entire purpose is serve as a menu with buttons that shows other usercontrols onclick, is it still a 'design-crime' to call a function on mainform (a function that shows usercontrols) from a user control? (like I did in 'update #1')
By adding controls from a user control to its parent you are breaking the separation of concerns principle and you are tightly coupling both controls/forms. As suggested by Hans Passant you should use events. Please refer to:
best practices to call methods in the parent form and access gui elements in parent form in c#

WPF : Definition Control Template by programmatically

We can make WPF Custom Control for overriding some control's look&feel, like icon button.
public class MyButton : Button {
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
...
}
But I think that way has a some problem. It causes a some issue when I distribute the Custom Cotrol 'MyButton'. Because MyButton dependent on external resource, the WPF Style MyButton. So I need to distribute two files : MyButton.cs and MyButton.WPF.
So, How can I definite a Conotrol Template by programmatically?
(Of cause, another way to solving the problem is making WPF User Control. but my point is not that.)
Note : I found some resources about this issue. That was a Inline XAML scripting. But to me, the XAML scripting is not option. Because I'm learning on WPF so I want to know WPF thatself, not a trick.
You do not distribute the code, you distribute a dll that contains the class and the generic.xaml
That way another designer/developer can 'override' the template and your template stays as a safe fall-back.
EDIT
Defining a Template in code is no fun (a lot of code, hard to debug, hard to maintain) but it can be done:
var template = new ControlTemplate(typeof(MyControl));
EDIT2
Another hack is to specify the template in a long string and use the XAML Parser to load it.

How to call a user control method using MVVM?

I'm working in a WPF project, I'm using the MVVM patter in my project.
I created a user control (also in WPF) and I want to use it in my project, now, my problem is that I have a method in my user control that I need to call from my View Model, but I don't know how to do that, how to bind to the method inside my control from the view model.
If I use code behind, obviously there is no problem since I have a direct reference to my control, so I can do "mycontrol.MyMethod();"m, but of course, doing in this way will go against the logic of the MVVM pattern.
I tried to use a Dependency Property in my user control, and use that Dependency Property to bind to it in the xaml of my project but it didn't worked, the compiler says that the property was not found or is not serializable.
So I will appreciate if someone can share some light about how can I accomplish this.
Edited
As far as I understand you have the view, which is all the GUI, then you have the model, which is all the logic, and them you have the view-model which is like an intermediate layer used to bind the view with the model, right?
In this way I have developed my project, however I came to the problem that I need a custom control, a TextBox that remember what the user entered, and when he start typing, if there are words that start with that letter, those words are shown as a suggestion, as Google does it.
This TextBox is used as a search filter; so I created a user control to do this, I added a method to my user control to allow whatever application that uses my control to add items to an internal array that holds all the strings that the user has entered.
I created a user control because I couldn't find any control that behaves the way I want.
So my problem is when I add my user control to the main project, because I need to someway be able to call the method that add the items to the internal array, but maybe I'm doing things the wrong way, so if any of you has a better idea, I will appreciate if you shared it with me.
You should never call View methods from ViewModel, and vice versa.
Make a property (ObservableCollection?) on your ViewModel, it will have CollectionChanged event, subscribe to it to monitor changes (if needed).
When you add an item to the collection in your ViewModel, GUI will be updated accordingly (you have to perform the Add() operation on GUI thread, btw).
If you need to change the current position in your list, there are colections for that (CollectionViewSource, etc).
If you really really need to pass a string to your control, make a DependencyProperty and bind it OneWay to your ViewModel's property. When you set the value, it will call PropertyChangedCallback on your DependencyProperty.
Why does the consumer of the user control need to maintain the control's internal array? That seems like you've exposed an implementation detail that you don't need to.
Why not simply make that array a dependency property (and an IEnumerable<string> or ObservableCollection<string> besides)? Then you can simply create the corresponding property in your view model and bind it to the control. It also makes the control considerably more versatile.
You shouldn't call something in the View from the ViewModel since that breaks the model.
If the reason you want to call the method in the user control is to do with UI only, I don't see anything wrong with doing it from the view - the view's cs and the view's xaml are in the same "space" in the model. You can be overly-purist in wanting to have lean and mean view cs files.

Designing Windows.Form with multiple panels -> How to hide one panel (like a PS layer)

How can I hide one panel in Visual Studio 2008 Form Designer like a layer in PS? Otherwise, can someone recommend another better method to design multiple "screens" that must be clicked through by the user?
What you describe is a wizard, and you might want to investigate the approach from Eric J.
However, when I have cases where I want to have multiple panels in the same space within my UI and I want to switch between them in the designer, I like to use a TabControl and hide the tabs on the TabControl. This makes the UI easier to manage at design time and the code is pretty simple to switch between the tabs at run time.
I made a custom control that derives from TabControl called HiddenTabsControl that is very simple. The class only overrides the WndProc and lets the TabControl base class handle everything else. All you need to do is:
Add a New Item to your project
Choose Custom Control,
Name it something like HiddenTabsControl.
Change the base Class to TabControl, remove the Constructor and the OnPaint override that Visual Studio added.
Copy this override for WndProc into the class:
protected override void WndProc(ref Message m)
{
// Hide tabs by trapping the TCM_ADJUSTRECT message
if (m.Msg == 0x1328 && !DesignMode)
{
m.Result = (IntPtr)1;
}
else
{
base.WndProc(ref m);
}
}
Now you can change tabs in the designer and design the UI easily and in the code you can handle events to change tabs as needed. Changing the Selected tab is easily done with:
this.hiddenTabsControl.SelectedTab = this.tabPageYouWantVisible;
One side effect of removing the tabs is the space that the tabs occupy when the control is constructed. Removing them will make the space the HiddenTabsControl occupies change by shrinking it. I usually set the Anchor of the HiddenTabsControl to bottom to keep it from shrinking.
I used this Wizard code in a recent project and it worked well.
It provides the basic experience you are after.
Another less elegant, but quick hack, approach is to simply not add the panel to the parent form until runtime. In doing that, the designer has no idea where the panel belongs prior to compilation, and it won't be displayed.
For example, find the block of code where you add controls to the parent form:
//this->Controls->Add(this->panel_X);
this->Controls->Add(this->tabControl);
this->Controls->Add(this->menuStrip_topMenu);
Comment or remove the statement, then find the handle to the event that occurs when the form is loaded:
this->Load += gcnew System::EventHandler(this, &MainForm::MainForm_Load);
Then in the definition of the event handler, add the control to the form:
System::Void MainForm_Load(System::Object^ sender, System::EventArgs^ e) {
...
...
this->Controls->Add(this->panel_X);
}
I haven't experienced any unwanted side effects by doing this, but if anyone has a good reason to not I'd be interested in hearing it.

How do I hide the input caret in a System.Windows.Forms.TextBox?

I need to display a variable-length message and allow the text to be selectable. I have made the TextBox ReadOnly which does not allow the text to be edited, but the input caret is still shown.
The blinking input caret is confusing. How do I hide it?
You can do through a win32 call
[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);
public void HideCaret()
{
HideCaret(someTextBox.Handle);
}
When using the win32 call don't forget to hide the cursor in the textbox's GotFocus event.
Just for completeness, I needed such a functionality for using with a DevExpress WinForms TextEdit control.
They already do provide a ShowCaret and a HideCaret method, unfortunately they are protected. Therefore I created a derived class that provides the functionality. Here is the full code:
public class MyTextEdit : TextEdit
{
private bool _wantHideCaret;
public void DoHideCaret()
{
HideCaret();
_wantHideCaret = true;
}
public void DoShowCaret()
{
ShowCaret();
_wantHideCaret = false;
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
if (_wantHideCaret)
{
HideCaret();
}
}
}
To use the code, simply use the derived class instead of the original TextEdit class in your code and call DoHideCaret() anywhere, e.g. in the constructor of your form that contains the text edit control.
Maybe this is helpful to someone in the future.
If you disable the text box (set Enable=false), the text in it is still scrollable and selectable. If you don't like the visual presentation of a disabled text box (gray background usually) you can manually override the colors.
Be warned, manually overriding colors is going to make your form/control look weird on systems that do not use the default color/theme settings. Don't assume that because your control is white that everyone's control is going to be white. That's why you should always use the system colors whenever possible (defined in the System.Drawing.SystemColors enumeration) such as SystemColors.ControlLight.
I know this is an old thread but it is a useful reference.
I solved the problem with a much easier but very kludgie solution, which may depend on how much control you have over the user's access to the form. I added a textbox (any focus-able control) which I gave prime tabIndex value and then positioned it off-form so that it was not visible. This works fine on a dialog because the user can't resize. If the form is resizeable, this may not work.
As I said, a kludge - but a lot easier to set up. (BTW I found the HideCaret approach didn't work - but I didn't pursue it hard.)
AFAIK, this cannot be done. The TextBox control is a funny control because it actually has a lot of behaviour that can't be modified due to the way it taps into the operating system. This is why many of the cool custom TextBoxes are written from scratch.
I am afraid you may not be able to do what you wish to do :(

Resources