Cannont Update control in WPF custom control - c#

I have created a custom control which holds a button in it. The button is styled, so as to hold a grid with two rows, an image in the first and a TextBlock in the second. I have written an Event Handler for the custom control. When the mouse enters the path of the object the MouseEnter event fires, where I try to change the TextBlock's FontSize and Foreground color, however the control does not update. In contrast, I have tried to modify an regular TextBlock's(not part a custom control and controltemplate) properties, and they update correctly, on the fly.
What am I missing here?? Here is the code for the event handler:
private void ThemeButton_MouseEnter(object sender, MouseEventArgs e)
{
InitializeProperties();
TextElement.FontSize = 16;
TextElement.Text = "new text";
TextElement.Foreground = Brushes.Red;
TextBlock element = MainWindow.FindChild<TextBlock>(MainWindow.StartWindow, "textField");
element.Text = "new text for regular textblock";
element.Foreground = Brushes.Red;
}
InitializeProperties is a methid that initializes TextElement(typeof TextBlock) and ImageElement(typeof Image) properties. They are not null. The properties are just regular .NET properties.

wow...my problem actually was that the Properties(TextElement and ImageElemenet) were pointing to the elements in the template(custom control) and not to the elements that actually got rendered in the Window...
Remember, when searching for elements, always start your search after rendering is complete(i.e. start search when a user action fires an event or something similar) so that the elements get into the Visual Tree!!

Related

How to change the BackColor of Buttons inside a TableLayoutPanel?

Is there any way to change the background color of Buttons inside a TableLayoutPanel?
The background color of the Buttons will be changed with a click of a Button outside of the TableLayoutPanel.
Actually I wanted to know how to identify Buttons which are inside a TableLayoutPanel.
I am providing a code block below. Please correct me.
private void button10_Click(object sender, EventArgs e)
{
Button btnClicked = sender as Button;
// wanted to convert the controls of tablelayoutpanel
if (tableLayoutPanel1.Controls is Button)
{
btnClicked = (Button)tableLayoutPanel1.Controls;
}
else
continue;
}
// Couldn't call the buttons inside the tablelayoutpanel.
Control.Controls is a collection. It cannot be cast to a single object. This:
tableLayoutPanel1.Controls is Button
will be notified in the code editor (green underline) with the message:
The given expression is never of the provided ('Button') type.
This cast will instead generate an error:
btnClicked = (Button)tableLayoutPanel1.Controls;
CS0030: Cannot convert type
'System.Windows.Forms.TableLayoutControlCollection' to
'System.Windows.Forms.Button'
To modify a property of all Button controls child of a TableLayoutPanel (or any other container), you can enumerate its Controls collection, considering only the child Controls of a specific Type.
For example, change to Color.Red the BackColor property of all Buttons inside a TableLayoutPanel:
foreach (Button button in tableLayoutPanel1.Controls.OfType<Button>()) {
button.BackColor = Color.Red;
}
Change to Text property of all Buttons in the first Row:
Note that, here, I'm using a generic Control type instead of Button. This is because the Text property is common to all controls that derive from Control. The Text property is defined in the Control class.
foreach (Control ctl in tableLayoutPanel1.Controls.OfType<Button>())
{
if (tlp1.GetRow(ctl) == 0)
ctl.Text = "New Text";
}
Modify a property of a Control in the first Row, first Column of the TableLayoutPanel:
Here, I don't know what kind of control is located at coordinates (0, 0), but I know it's an object derived from the Control class. So I can set a property that belongs to this class and is threfore inherited.
It may happen that a specific property is not relevant for a control Type. In this case nothing will happen (you can try to set the Text property of your TableLayoutPanel).
(tableLayoutPanel1.GetControlFromPosition(0, 0) as Control).BackColor = Color.Green;

How to dynamically remove a UserControl from the form in c #?

I have a UserControl that is dynamically added to a FlowLayoutPanel. In that same UserControl I have a button to remove itself if the user wants it, obviously at runtime. To eliminate I mean not only to eliminate that tight button, but also the full UserControl that contains the button.
The code of when the UserControl are added dynamically at the moment is as follows:
private void agregaUC() {
UserControl1 UC = new UserControl1();
aux += 1;
UC.Tag = aux.ToString();
flowLayoutPanel2.Controls.Add(UC);
}
The code to eliminate this is on the side of the form, that is, where the UserControl are being added. The button event to remove the UserControl is thrown by code through the operator + =, then there I write the suggestions that you give me.
EDIT: Based on the sample of code you've added, I've modified the below code to work better with what you are looking for. You need to find out how to access the Tag of the control you're trying to remove.
Since you don't have a reference, then you should make sure that the .Tag property can be found, because then you can do something like
foreach (Control c in flowLayoutPanel2.Controls) {
if (c.Tag == "Aux") {
flowLayoutPanel2.Controls.Remove(c);
c.Dispose();
break;
}
}
EDIT
Reading through all the comments everywhere, it seems like this is what's happening. There is a UserControl, inside that user control is a Button (Delete) and the button's Click event is subscribed to by the window, and it's in this event handler that we're trying to remove the UserControl from flowLayoutPanel2
Based on these assumptions, your function should look like this:
void UserControl_Delete_Click(object sender, EventArgs e)
{
Button Delete = (Button)sender;
UserControl UC = (UserControl)Delete.Parent;
flowLayoutControl2.Controls.Remove(UC);
UC.Dispose();
}
This is assuming a lot about the internal structure of everything, as I don't have the code to confirm this will work. It will get you a long ways down the path, though, and should only need a little tweaking based on the actual structure of the UserControl.
You can try something like that.
this.Parent.Controls.Remove(this);
Control.Parent Property.
Remark: Setting the Parent property value to null removes the control from the Control.ControlCollection of its current parent control.
So
this.Parent = null;
Edit
The code is intended to be called from within the user control itself.

LostFocus not raised for a TreeViewItem

In my WPF C# project, I've created a TreeView. Each TreeViewItem has a LostFocus event that must be raised when item lost its focus.
I've also create a button that is needed to be clicked when user wants to change header of a certain TreeViewItem.
User, after the selection in TreeView, can click on button and a TextBox appear replacing the TreeViewItem header.
If user does not click on TextBox but click on another TreeViewItem, the LostFocus event is never raised. Otherwise, if user click on TextBox and then change focus, it is raised.
I've also used textBox.Focus() and Keyboard.Focus(textBox) but the do not work.
How can I fix this?
Just to be clear, before creating a post I've read another SO answer here
Here is the snippet code
private void RenameButton_Click(object sender, RoutedEventArgs e)
{
TreeViewItem twItemSelected = (TreeViewItem)this.Treeview_PropertyDefinition.SelectedItem;
var textBox = new TextBox()
{
Text = (String)twItemSelected.Header,
};
textBox.Focus();
Keyboard.Focus(textBox);
if (textBox.IsFocused)
MessageBox.Show("focused");
twItemSelected.Header = textBox;
//check which property is currently selected
String parentName = ((TreeViewItem)twItemSelected.Parent).Name;
((TreeViewItem)twItemSelected.Parent).Parent).Name;
//get values from file
//show page based on parent value
switch (parentName)
{
case "RectangleBar_TreeviewItem":
textBox.LostFocus += (o, ev) =>
{...}
}
I recommend you change your UI to use a Trigger on your TreeViewItem to replace your HeaderTemplate based on a property you define in the TreeViewItem. Set this property true when the item is double-clicked. Set it false when IsKeyboardFocusWithin goes false (you can override metadata and add a PropertyChangedCallback for this).
As far as your LostFocus problem goes, I suspect your problem is that you have multiple focus scopes.
Additional details on doing this the "WPF way"
Here are some of the details on how to implement this using an attached property, triggers and templates.
Your templates can be as simple or as complex as you want. Here's simple:
<DataTemplate x:Key="NormalTemplate">
<ContentPresenter />
</DataTemplate>
<DataTemplate x:Key="TextBoxTemplate">
<TextBox Text="{Binding}" />
</DataTemplate>
Here is what your style would look like:
The attached property "ShowTextBox" can be created in MyWindowClass using the "propa" snippet - just type "propa" and hit tab, then fill in the blanks.
To switch the item to show the textbox, just:
SetShowTextBox(item, true);
To switch it back:
SetShowTextBox(item, false);
Please try learning and investing in the patterns and practices of WPF so that it'll be easy to do what you want to achieve.
There are also projects out there that can help you get started with what you want to achieve with TreeViews.
Sample project

Center Checkboxes in Table Layout Panel

I'm trying to center my checkboxes in a TableLayoutPanel, but they always end up looking left-aligned due to the nature of the checkbox control. See picture below:
I want each rows checks to be left-aligned, but for it to appear more centered. Something like the following:
I've checked around online, and I can center the checkboxes by setting AnchorStyles.None which is not what I want, because then the checkboxes are not aligned. I have them set to Dock.Fill so you can click anywhere in the cell to activate the checkbox.
I'm currently just padding my table to achieve a similar effect, but it's by far not an acceptable solution long-term. Also, padding the cells will line break the checkbox text without taking up all the available space on the row (since some of the row is being eaten by padding). The same goes for using a spacer-cell on the left of the table, not an ideal solution.
Does anyone have any ideas? Thanks!
This may work for you:
Set all the ColumnStyles of your TableLayoutPanel as .SizeType = SizeType.AutoSize.
Set your TableLayoutPanel.AutoSize = true and TableLayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
Add this code to center your checkboxes (as well as your TableLayoutPanel) dynamically:
//SizeChanged event handler of your tableLayoutPanel1
private void tableLayoutPanel1_SizeChanged(object sender, EventArgs e){
//We just care about the horizontal position
tableLayoutPanel1.Left = (tableLayoutPanel1.Parent.Width - tableLayoutPanel1.Width)/2;
//you can adjust the vertical position if you need.
}
UPDATE
As for your added question, I think we have to change some things:
Set your CheckBox AutoSize to false. The solution before requires it to be true.
Add more code (beside the code above):
int checkWidth = CheckBoxRenderer.GetGlyphSize(yourCheckBox.CreateGraphics(),System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal).Width;
//TextChanged event handler of your CheckBoxes (used for all the checkBoxes)
private void checkBoxes_TextChanged(object sender, EventArgs e){
UpdateCheckBoxSize((CheckBox)sender);
}
//method to update the size of CheckBox, the size is changed when the CheckBox's Font is bolded and AutoSize = true.
//However we set AutoSize = false and we have to make the CheckBox wide enough
//to contain the bold version of its Text.
private void UpdateCheckBoxSize(CheckBox c){
c.Width = TextRenderer.MeasureText(c.Text, new Font(c.Font, FontStyle.Bold)).Width + 2 * checkWidth;
}
//initially, you have to call UpdateCheckBoxSize
//this code can be placed in the form constructor
foreach(CheckBox c in tableLayoutPanel1.Controls.OfType<CheckBox>())
UpdateCheckBoxSize(c);
//add this to make your CheckBoxes centered even when the form containing tableLayoutPanel1 resizes
//This can also be placed in the form constructor
tableLayoutPanel1.Parent.SizeChanged += (s,e) => {
tableLayoutPanel1.Left = (tableLayoutPanel1.Parent.Width - tableLayoutPanel1.Width)/2;
};
Instead of having the checkboxes in cells, having each one inside a panel all inside a groupbox will allow the checkboxes to fill each panel and have a click able area around them. then with the groupbox dock set to fill and the panels' anchors set to top,bottom they all stay centered.

how can i achieve this in c#

i'm developing a windows form application.in the form, the left part is a tree menu, and the right part is show area. how can i change the show area according to what i click on the tree menu.
(source: 126.net)
i use treenode class to implement treemenu like this:
System.Windows.Forms.TreeNode treeNode27 = new System.Windows.Forms.TreeNode("basic operation");
what i try to do is use several panels. each panel bounds to a menu item. by setting the visible property, i can achieve that goal. but it is too inconvenient.especially when i try to design each panel.
any good suggestion?
You could design each "Panel" as a new User Control. That way you can design all of the "panels / areas" on their own, independently of the Main Form.
On your Main Form, create a single panel for the right hand side area and add all of the controls to that one panel.
Then when the TreeNode selection event happens you can set all the user controls to .Visible = false; except for the one you are showing and set that to .Visible = true; and .Dock = DockStyle.Fill;
What you need is an event handler that will be called at the time of the user clicking the treeview (Use TreeView from the toolBox). You can do that by selecting the treeview on the design page and under properties click on Events. Then select NodeMouseDoubleClick or NodeMouseClick depending upon what you want. Below is a code that captures the values selected...Enjoy...;)
private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (treeView1.SelectedNode.Level == 2)
{
//text on the first level
string text = treeView1.SelectedNode.Text;
}
else if (treeView1.SelectedNode.Level == 1)
{
//text on the second level
string text = treeView1.SelectedNode.Text;
}
}

Categories