Fairly new to WPF.
I'm tasked with converting our application with about ten different windows into a single-window, multi-tabbed application. I'd like your input on the most effective way of doing this.
I suppose I could do this the most-straightforward way -- copying and pasting a ton of code into the main XAML file. I imagine I'd end up with a gigantic file though, and would like to avoid that.
I should mention that the contents of each tab will be substantially different from each other.
There must be some element of WPF that enables this that I just haven't been able to find. Really appreciate any input.
In XAML there are user controls which you can use. That way, you could use one user-control per tab-content and let the main window only contain the tabview and tabitems and some minor logic code.
To achieve that, you can simply copy-paste most of your current window-code in one user control per window. The code base could remain almost the same (if there are no interactions between the windows at least).
There are quite many resources out there containing further details on user controls like this code-project article (a bit old, but the majority of its contents will still apply) or that MSDN one.
Related
I've thoroughly checked the custom controls topic, spent several hours looking into custom controls written by other people. I've written my own custom button, to feel it better. I've read all the google answers around the "why custom controls", "advanced custom controls examples" and such.
My question is, WHY?
Why would I (or anybody) go through 9 circles of hell to create his own custom control, when one can just adjust an existing control to his needs (using styles and templates). I actually didn't find any explanation on google, just tons of examples, mostly from people who sound even less educated than me.
I imagine there IS such need, when talking about some complicated DataGrid with, I don't know, every cell being a button or something (and still I believe I could do it with a regular DataGrid)... But I've not found anything more complex than a beautiful button. Is there nobody sharing a complicated code on the topic?
There are different levels of element customization in WPF, depending what class you extend from. Each has its own uses and is implemented differently. It is not clear from your question if you are asking about a specific type of control or about all of them in general. So, I will tell you what I think about different ones.
UIElement or FrameworkElement
Extending UIElement gives you the lowest level custom control where you have complete control over the layout and rendering. FrameworkElement is slightly higher level as it does most of the common layout stuff for you while also allowing you to override key parts of it. The main idea with these is that they do their own rendering rather than composing other elements together.
I have made a number of custom FrameworkElements over the years. One example is a ruler similar to one you might find in a program like Photoshop. It has a bunch of properties providing customization for how it is displayed as well as showing markers indicating mouse position relative to the ruler (and a number of other little optional features). I have used it in two different professional projects. I think the main benefit is that it is extremely easy to drop in and set properties/bindings on wherever desired. Build it once, use it over and over.
Control
Extending Control introduces the concept of compositing multiple elements/controls into one reusable component via control templates.
I have used this one less often, but still find it very valuable in the right circumstances. Again, the main benefit here is reusability. You create a control with properties that make sense for what you want to do, then hook up those properties to the properties of the controls in it's control template. Really, this is the same as applying a new template to an existing control, with the added feature of being able to define your own dependency properties. You also have the ability to perform custom logic in the control's code if you need to.
I may be misreading some of your text, but you seem to imply that making a custom control is considerably more difficult than making a control template for an existing control. I have found that the two are nearly identical in most cases using this approach, the only difference being whether you have a code behind you can use.
User Control
A user control is really only slightly different from a custom control practically speaking. Only, instead of defining a control template, you define the visual content directly.
This is probably the most common type of custom control. It is basically the standard method for making XAML based content in a WPF application. These can be reused like other controls, but are more suited for single use such as the content of a dialog or window or something else that is specific to a single application.
Some Other Control
You can also extend an existing control to add additional functionality to it. This way, you still get all the features the control offers and only have to implement the additional bit.
For example, I have a custom control called an AutoScrollRichTextBox that extends RichTextBox. So, it does everything a RichTextBox can do. It also has the ability to automatically scroll to the bottom when content is added to the text box (which it only does if the text box was already scrolled to the bottom before the addition content was added).
I could have implemented that feature as an attached property instead of an extension of the control (and maybe I should have), but it works, and I have used it in three different applications (as an output window and as a chat log). So, I am happy with it.
In the end, it really is just a matter of how self-contained, reusable, and easy to drop in you want a control to be. If there is already a control that does what you want, and you just want it to look different, then you should definitely use styles and templates to achieve that. However, if you want to make something that doesn't already exist, limiting yourself to using only styles and templates will make the implementation work harder and make the end result less reusable and more difficult to set up additional instances (unless all instances are identical).
The examples of making things like buttons that look different are not examples of what you should use a custom control for. They are just examples of how someone would go about making a custom control for the purpose of teaching the details of the process. If you actually want a customized button, just customize a button.
I am building a wpf user control to provide navigation facilities for database records.
The control is provided with a set of default images (as illustrated above) which the end user can change is they so wish. In addition the end user can choose to dispense with images altogether. In the event that they select that option (for either one or all of the buttons that comprise the control) I have provided some default fallback text.
This text can also be overwritten by the end user if they so wish, but the default text at least provides them with some basic text that essentially conveys what the button does and saves them having to add text every time they use the control (default tooltip text is also provided).
Now if you happen to speak English, or your intended target audience is English this should work, but it doesn't really cater as is for languages other than English. This I would now like to change.
What reading I've done on the subject of multi-lingual resources and wpf seems to assume that one is talking about the overall application rather than a standalone user control that might be used in different language environments.
I had a talk with a creator of controls who said that making this multilingual would probably involve building several copies of the control for each intended language.
In the light of this I have two questions. Was the gentleman I spoke to correct, should I in fact build multiple copies of this for each language, of is there a way to have multi-language resources within the same copy of the user control?
If the latter is possible what is the correct way to go about achieving this. We will be dealing in total with default texts for eleven buttons (which I will need to be able to refer to in code within the control incidentally) and default texts for thirteen tooltips (which again will need to be able to be referred to within the code of the control).
Take a look on WPF localization extension.
Here's a pretty good documentation for it: link.
You can define your controls' localizable properties, which store their localized values in the satellite resource assemblies.
In your xaml code, define the localized properties with xaml extensions syntax:
<Button Content="{lex:Loc Test}" />
Then, create resource files for each culture your application will support and give them the same name as the main assembly plus the general or specific culture code (e.g. en-US, de, de-AT, ...) before the .resx ending yielding: AssemblyName.CultureCode.resx.
Now, populate the resource files with your localized properties key/value pairs and build the project.
You're done!
I searched and Googled first, thinking surely someone must have asked this before, but I sure can't find a good description of this problem.
I have six or eight similar C# .NET 2.0 WinForms applications built with the fairly common model of a main application window with several GUI data fields plus several modal dialogs for further data collection. Many of the data fields (especially TextBoxes) have identical data validation routines. I'm writing the same xxx_Validating() routines over and over, which in the simplest case only capitalize the first character of the entered text (if any) and redisplay the result. I have another one for ZIP Code fields that takes the first 3 digits of a 5-digit US postal ZIP Code and returns the corresponding State, using a 1000-member string array. Simple stuff. There are a few others; here's an example:
public void CapFirstCharTextBox_Validating(object sender, CancelEventArgs e)
{
string strValue = ((TextBox)sender).Text.Trim();
if (strValue.Length >= 1) {
if (char.IsLower(strValue[0])) {
strValue = strValue.Substring(0, 1).ToUpper() + strValue.Substring(1);
((TextBox)sender).Text = strValue; // fires (whatever sender)_TextChanged()
}
}
}
Again this is part of a half-dozen or so such "utility" routines. I've only got one set of these per dialog box class, and all the various TextBoxes in that dialog which need this have their Validating event pointing to the same method. So it's not like I've got 20 of these in a given source file (one for each TextBox) or anything; there's only one for the whole dialog class.
Problem is, the whole set of these exists in every source file where I need them. That's one set for the main window and more for each pop-up dialog box -- and that's too many. I understand modal dialog box classes can't communicate with each other, and making all this stuff global is elusive at best and a big "no-no" at worst.
I have successfully tried passing a reference to "FormMain" (where one copy of these routines exist) to the various dialog constructors, and then calling these validation routines with that from their own validation handlers. It works but feels awfully clunky and certainly not like the best approach.
So, how would I (or would I want to) rearrange the project and organize the code better to have only a single instance of these kinds of things? How would I wire up a global "utility" class of such methods such that I can get to it from the main form's code and from that of a bunch of pop-up modal dialog boxes as well?
I'd like to maintain just one executable with no additional .DLLs if possible (these are all one-project-per-solution, by the way), and if practical I'd like to further be able to share that common code across multiple solutions.
I think the answer will include writing new assemblies, using different namespaces (currently all my code in a given project is contained in the same namespace), and maybe separating this stuff out into its own project in the same solution file.
Is it possible?
You can share code across solutions by keeping the code in one place and adding a link to the file in each solution.
To add a link: right click the project (or folder) you want to add the code to, then select "Add existing item", browse for the file, when found click the down arrow on the button and pick Link to.
This way the projects that link to the file will share the same code.
BTW: take care when using a source control system that doesn't know how to handle these links.
I am new to WPF environment and I am experiencing some problems like if there are alots of things how do we manage them.for example I have three borders each of same size same location and they contains controls like textboxes etc etc we construct them sequentially but when it comes to edit we get in trouble modifying the border that is at bottom.
So in short how do we manage many controls on single page so that it remains easy to edit
Not sure I completely understand your concerns, but here are several point that make editing WPF UI pretty easy:
Correct usage of layout panels. If you will use approach with absolute positions for each control then it might become a nightmare to move or resize some of your controls. Correct layout (and panels such as DockPanel/StackPanel/etc) might help you a lot.
Incapsulating repeatable parts. WPF has a lot of feature to avoid repeating UI code. I'm talking mostly about Styles and Control templates at the moment. If you have your borders repeating through the entire window, maybe you should think on extracting this border as a ControlTemplate for ContentControl for example?
but I've found that encapsulating controls such as borders, textboxes etc in User Controls helps to keep things well managed (not to mention helps reduce code), similarly using a Resource Dictionary to store styles/animations is useful for very big projects (remember though that the local resources will take precedence when applied so remove them if they not in use)
furthermore, using Layout Panels such as Stacks,Grids and Dockpanels allows you to collapse User Controls when not needed or otherwise (also I've found that for some reason, Grids allow controls to overlap (when items are not correctly ordered in Grid Rows and Columns) which can lead to some elements not being seen in design.
Plan your layout properly and think through which Panels would be best for them, having to go back much later and change can be annoying (though admittedly it happens).
Also remember to use partial classes to properly structure your stuff, having to read through 1000+ lines of code to find something can be a nightmare.
In order to make a convenient UI for an .Net 2.0 Winforms application I am working on, I have need for a control that I'm pretty sure goes beyond the "out of the box" behavior of any standard control. A mock-up of what I'm trying to achieve follows:
Mock up http://www.claware.com/images/temp/mockup.png
Essentially, this part of the application attempts to parse words into syllables from tribal languages (no dictionary to refer to; any and all unicode characters are possible.) By the time the user gets this far, he has already defined the vowels / consonants in his language and some other configuration. There is then an iterative process of (1) the application guesses which syllables exist in the language based on some rules, (2) the user refines the guesses, selecting the correct parsings or manually parsing a word, (3) the application "learns" from the user's feedback and makes smarter guesses, (4) repeat until the data is "good enough" to move on.
The control needs to present each word (the grey headers), then all the syllable break guesses (the white areas with dots separating the parts of words.) There is also a way to manually enter a parsing, which will display a text area and save button (at the bottom of the mockup.) When the user hovers over a guess, the background changes and "accept / reject" buttons appear. Clicking on the accept, or entering a manual parsing, removes the entire word from the list. Clicking the reject button removes just that item.
I'm by no means 100% sold on the formatting I have above, but I think you can get a general idea of the types of formatting and functional control I need. The control will also scroll vertically--there may be thousands of words initially.
My question for you experienced WinForms developers is: where to start? I would really, really like to stay within the .Net core framework and extend an existing control as opposed to a third-party control. (At the risk of starting a religious war: yes, I suffer from NIH-syndrome, but it's a conscious decision based on a lot of quick-fix solutions but long-term problems with 3rd party controls.) Where can I get the most "bang for my bucK" and the least reinventing the wheel? ListView? ListBox? ScrollableControl? Do I need to go all the way back to Control and paint everything manually? I appreciate any help that could be provided!
[Edit] Thanks everyone for the ideas. It seems like the most elegant solution for my purposes is to create a custom control consisting of a FlowLayoutPanel and a VScrollBar. The FlowLayoutPanel can contain instances of the custom controls used for each word. But the FlowLayoutPanel is virtual, i.e. it only contains those instances which are visible (and some "just out of scroll"). The VScrollBar events determine what needs to be loaded. A bit of code to write, but isn't too bad and seems to work well.
I would look at the TableLayoutPanel and FlowLayoutPanel controls. These will let you organize a series of controls with moderate ease in a vertical fashion. I would then create a UserControl that consists of a label and 2 buttons. The UserControl will expose properties like Text and events that are exposed for the button clicks.. For each entry in the list, you will create an instance of the UserControl, assign the text value, and handle the click events. The instance will be placed in the Table/Flow panel in the correct order. Both of those layout panels do allow for inserting items between other items so you can add/remove items from the list dynamically.
Edit:
Given the length of what you are trying to render, I would consider using the DataGridView and do some custom rendering to make it perform how you want it to work. Using the rendering events of the DGV you can merge columns, change background colors (like highlighting the dark gray lines), turn on/off the buttons, and handle changing the grid into edit mode for your rows to allow modification or inserting of new values. This method would easily handle large datasets and you could bind directly to them very easily.
Well, this certainly looks like a candidate for a custom component that you should be creating yourself. You can create this using standard .Net drawing commands along with a text-box, and a regular button control.
Now you want to find out where to start.
Create a Windows Forms Control Library project.
Drop in the textbox and the button control.
The panel drawing code should preferably be done by code. This can be done using the regular GDI+ commands.
Edit:
Here's another idea, and one that I've practically used in my own project with great success.
You could use a web-browser control in the app, and show your data as html. You could update the source of the web-browser control based on the input in the textbox, and clicking on the links in the web browser control will give you the event that you can trap to do some action. Your CSS will work.
I used this technique to build the 'desktop' in an app I made called 'Correct Accounting Software'. People loved the desktop so much that it is one of the best loved features of the app.
Here's how I would do it:
Create a custom control. In this custom control, have a ListBox atop a LinkButton, and when the LinkButton is clicked you can make it give way to a TextBox. The ListBoxes will have the top row unselectable... you can probably get the rest from there. When you get your list of words, fill a Scrollable of some kind with one control for each word:
(foreach String word in words){
myScrollable.add(new MyComponent(word));
}
From there, I'm not sure what you want to do with the boxes or the data, but that's my initial idea on the UI setup.
Use the WebBrowser control and generate the HTML markup into it using DocumentStream or DocumentText.