WPF SurfaceButton that drops down SurfaceScrollViewer on click - c#

As the title says, I'd like to have a component whose base is SurfaceButton, and when clicked will drop down a SurfaceScrollViewer. I think I've made the proper first steps, but the SurfaceScrollViewer never becomes visible, nor does it seem to show in Blend's hierarchy. Am I accessing or initializing something incorrectly? I have multiple instances of SurfaceDropDown objects as children of a StackPanel, which is the content of another SurfaceScrollViewer. This parenting scroll viewer is aligned in a grid, which is why I set the heights and widths using the ActualHeight/Width properties.
public class SurfaceDropDown : SurfaceButton
{
public SurfaceScrollViewer scrollViewer;
private StackPanel stackPanel;
public SurfaceDropDown()
{
this.Click += OnClick;
scrollViewer = new SurfaceScrollViewer();
scrollViewer.Height = this.ActualHeight * 5.0;
scrollViewer.Width = this.ActualWidth;
scrollViewer.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
scrollViewer.VerticalAlignment = System.Windows.VerticalAlignment.Center;
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
stackPanel = new StackPanel();
for (int i = 0; i < 5; ++i)
{
Label label = new Label();
label.Content = "Label " + i.ToString();
stackPanel.Children.Add(label);
}
scrollViewer.Content = stackPanel;
scrollViewer.ScrollChanged += OnScroll;
this.AddChild(scrollViewer);
}
private void OnClick(object sender, RoutedEventArgs e)
{
scrollViewer.Visibility = scrollViewer.Visibility != Visibility.Visible ? Visibility.Visible : Visibility.Collapsed;
}
private void OnScroll(object sender, RoutedEventArgs e)
{
}
}
Any help and/or suggestions are greatly appreciated!

Related

C# Windows Forms Application ListBox Controls shifted

I'm having problems with an owner drawn listbox in a windows forms application.
The listbox is filled with objects containing their own UserControl. The user control of each itemis shown in the listbox.
This all works but when I scroll up or down the UserControls appear shifted a bit.
Once I click them, they jump to the right position.
In the picture you can see the white UserControls shifted a bit to the right and a bit down.
This is how they look before scrolling.
The list is filled with objects of this type:
class Class1
{
public UserControl1 UC;
public string Text;
public Class1(UserControl1 uc, string text)
{
UC = uc;
Text = text;
}
}
This is the class that controls the list:
class ListDrawer
{
public ListBox LB;
public int HeaderHeight = 25;
public ListDrawer(ListBox lb)
{
LB = lb;
LB.DrawMode = DrawMode.OwnerDrawVariable;
LB.DrawItem += LB_DrawItem;
LB.MeasureItem += LB_MeasureItem;
}
private void LB_MeasureItem(object sender, MeasureItemEventArgs e)
{
ListBox lst = sender as ListBox;
Class1 c = (Class1)lst.Items[e.Index];
e.ItemHeight = HeaderHeight;
e.ItemHeight = e.ItemHeight + c.UC.Height;
}
private void LB_DrawItem(object sender, DrawItemEventArgs e)
{
ListBox lst = sender as ListBox;
Class1 c = (Class1)lst.Items[e.Index];
e.DrawBackground();
e.Graphics.FillRectangle(Brushes.DarkSeaGreen, e.Bounds);
e.Graphics.DrawString(c.Text, LB.Font, SystemBrushes.HighlightText, e.Bounds.Left, e.Bounds.Top);
if (!lst.Controls.Contains(c.UC))
{
lst.Controls.Add(c.UC);
}
c.UC.Top = e.Bounds.Top + HeaderHeight;
}
}
The list is filled on a button click:
private void button1_Click(object sender, EventArgs e)
{
UserControl1 uc = new UserControl1();
Class1 c = new Class1(uc, "text 1");
ListDrawer LD = new ListDrawer(listBox1);
listBox1.Items.Add(c);
uc = new UserControl1();
c = new Class1(uc, "text 2");
listBox1.Items.Add(c);
}
Hope this can be fixed....
Cheers,
Robert.
In the user control override the onMove event:
protected override void OnMove( EventArgs e ) {
base.OnMove( e );
this.Parent.Invalidate();
}
It will probably flicker but will solve your problem

Change background of a button when clicking another button

I am currently experimenting in WPF and just created a UniformGrid with 800 buttons which are created in a for loop. All buttons have their own names and share the same click event.
What I want to do now is the following: I want to click the first button (rect0) to change the color of this button and the next one (rect1).
I am totally stuck right now because everything I write into the click event refers to the button I clicked.
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 800; i++)
{
Button BTN_rect = new Button()
{
Name = "rect" + i,
Background = Brushes.White,
};
BTN_rect.Click += BTN_rect_Click;
Uniform.Children.Add(BTN_rect);
}
}
private void BTN_rect_Click(object sender, RoutedEventArgs e)
{
Button BTN_rect = sender as Button;
BTN_rect.Background = Brushes.Red;
MessageBox.Show(BTN_rect.Name);
}
There are a load of ways to do this.
I took a shortcut and put just 9 buttons in a stackpanel, otherwise the same.
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 8; i++)
{
Button BTN_rect = new Button()
{
Name = "rect" + i,
Content =Name,
Tag = i,
Background = Brushes.White,
};
BTN_rect.Click += BTN_rect_Click;
sp.Children.Add(BTN_rect);
}
}
private void BTN_rect_Click(object sender, RoutedEventArgs e)
{
Button current = sender as Button;
current.Background = Brushes.Red;
string targetName = $"rect{((int)current.Tag) + 1}";
Button nextButton = sp.Children.OfType<Button>().Where(x => x.Name == targetName).SingleOrDefault();
nextButton.Background = Brushes.Red;
}
Usually, you'd template data into repeated controls rather than add them in code, btw.

Adding Custom Controls to a panel programatically with margin

Hello I made a custom text box with an associated label.
and I have a custom Form.
when on a form if I drag drop my custom textbox from the toolbox and set it's properties I can see that it works. However what I would like to do is when I'm on my custom control where I have a TableLayoutPanel (with 3 rows)
on row index 1(mid row) I would like to add my custom controls programatically.
when I do this the text label is somewhere else then the textbox.
My Code and the Image to my problem is below:
MyCustomTextBox:
public class MyLbTextBox : TextBox
{
#region CustomProperties
private Label AssociatedLabel = new Label();
private string _myLbText;
public string MyTextLabel
{
get => _myLbText;
set
{
_myLbText = value;
AssociatedLabel.Text = _myLbText ?? _myBindingField;
Size s = TextRenderer.MeasureText(AssociatedLabel.Text, AssociatedLabel.Font);
AssociatedLabel.Location =
new Point(Location.X - s.Width - AssociatedLabel.Padding.Right, Location.Y);
var MyMargin = this.Margin;
MyMargin.Left = 100;
this.Margin = MyMargin;
}
}
#endregion
private string _myBindingField;
public string MyBindingField
{
get { return _myBindingField; }
set
{
_myBindingField = value;
}
}
private MyJoins.MyExpressions _myExpression;
public MyJoins.MyExpressions MyExpression
{
get => _myExpression;
set => _myExpression = value;
}
public MyLbTextBox()
{
_myExpression = MyJoins.MyExpressions.Equals;
ParentChanged += MyLbTextBox_ParentChanged;
LocationChanged += MyLbTextBox_LocationChanged;
Disposed += MyLbTextBox_Disposed;
}
private void MyLbTextBox_Disposed(object sender, EventArgs e)
{
AssociatedLabel.Dispose();
}
private void MyLbTextBox_LocationChanged(object sender, EventArgs e)
{
Size s = TextRenderer.MeasureText(AssociatedLabel.Text, AssociatedLabel.Font);
AssociatedLabel.Location =
new Point(Location.X - s.Width - AssociatedLabel.Padding.Right, Location.Y);
}
private void MyLbTextBox_ParentChanged(object sender, EventArgs e)
{
AutoAddAssociatedLabel();
}
private void AutoAddAssociatedLabel()
{
if (Parent == null) return;
AssociatedLabel.Padding = new Padding(3);
Size s = TextRenderer.MeasureText(AssociatedLabel.Text, AssociatedLabel.Font);
AssociatedLabel.Location =
new Point(Location.X - s.Width - AssociatedLabel.Padding.Right, Location.Y);
Parent.Controls.Add(AssociatedLabel);
}
}
By the way, this is how I add my controls:
after adding my controls through the property grid
this is how I set them on the screen
private void _mySearchFields_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_mySearchFields == null) return;
foreach (var searchField in _mySearchFields)
{
if (MySearchFieldsPanel.Contains(searchField.MyControl)) continue;
MySearchFieldsPanel.Controls.Add(searchField.MyControl, 1, 0);
}
var myHeight = MySearchFieldsPanel.Controls.Cast<Control>().Sum(variable => variable.Height);
MyBdPanel.RowStyles[1].Height = myHeight + 40;
}
I appreciate any help
This line is a reason of all problems:
Parent.Controls.Add(AssociatedLabel);
This is bad idea if you are creating composite controls (composite = consisting from several real controls). That will cause layout problems you have experienced and more.
Instead consider either:
Utilize UserControl to create composition.
Create custom control (like you do) but without more controls. If you need label - draw it as text in OnPaint while allocating some space: fixed with margin, adjustable with some property or dynamic with measuring text.

C# StackPanel: Old Items Remain After Updating Values

Hey there StackOverflow,
I am having an issue with a certain portion of my Windows Store app. This is initially what is displayed...
and this is what happens when the button is clicked
As you can see, the show more button and the original string is still present. I have tried making the "Show More" button update the TextBlock to a "" value and the "Show More" button is still there and clickable. I have also tried clearing the StackPanel and re-adding the items to no avail.
Here is the code I used to do this:
private async void Description_Loaded(object sender, RoutedEventArgs e)
{
await wiki;
if (wiki.Result.ContainsKey("real_name"))
{
TeamInfoTitle.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
TeamDescription.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
PopulateInfo(Description);
}
else if(wiki.Result.ContainsKey("current_members"))
{
CharacterInfoTitle.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
Description.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
PopulateInfo(TeamDescription);
}
}
void expand_Click(object sender, RoutedEventArgs e)
{
HyperlinkButton button = (HyperlinkButton)sender;
button.Content = "Show Less";
button.Click -= expand_Click;
button.Click += shrink_Click;
TextBlock text = (TextBlock)button.Tag;
text.Text = (string)text.Tag;
}
void shrink_Click(object sender, RoutedEventArgs e)
{
HyperlinkButton button = (HyperlinkButton)sender;
button.Content = "Show More";
button.Click -= shrink_Click;
button.Click += expand_Click;
TextBlock text = (TextBlock)button.Tag;
string item = (string)text.Tag;
text.Text = item.Substring(0, item.LastIndexOf(" ", 150, 15)) + "...";
}
private void PopulateInfo(Grid desc)
{
for (int i = 0; i < desc.RowDefinitions.Count; i++)
{
string value;
if (wiki.Result.TryGetValue((desc.Children[i] as TextBlock).Name, out value))
{
FrameworkElement now = new TextBlock() { Text = value, FontSize = 24, TextWrapping = TextWrapping.Wrap };
if (value.Length > 200)
{
TextBlock text = (TextBlock)now;
HyperlinkButton expand = new HyperlinkButton() { Content = "Show More", HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Right, VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Bottom };
expand.Tag = text;
expand.Click += expand_Click;
StackPanel stack = new StackPanel();
text.Tag = value;
text.Text = value.Substring(0, value.LastIndexOf(" ", 150)) + "...";
stack.Children.Add(text);
stack.Children.Add(expand);
now = stack;
}
Grid.SetRow(now, i);
Grid.SetColumn(now, 1);
now.Margin = new Thickness(0, 0, 0, 10);
desc.Children.Add(now);
}
else
desc.Children[i].Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
Any help would be greatly appreciated! Note: Description and TeamDescription are Grids defined in the XAML.
Turns out the XAML code was triggering the loaded event twice, thanks swistak for putting me on the right track!

How to remove a single child of a single canvas after I added it?

I have a WPF project where i have to create a few ellipses on a canvas. I create two check Box and when i check on the first checkbox, the red ellipse will show on the canvas. If i uncheck the first check box, the ellipse will be disappear....the second check box will have the same function by creating a blue ellipse instead.
So heres my situation, when the two checkboxes are checked, a blue and a red ellipse will appear. To clear the ellipse on the canvas, i use the myCanvas.children.clear(). But when i uncheck one of the checkbox, both of the ellipse will be deleted.
private void redCB_Checked(object sender, RoutedEventArgs e)
{
drawRedCircle();
}
private void redCB_Unchecked(object sender, RoutedEventArgs e)
{
myCanvas.Children.Clear();
}
private void blueCB_Checked(object sender, RoutedEventArgs e)
{
drawBlueCircle();
}
private void blueCB_Unchecked(object sender, RoutedEventArgs e)
{
myCanvas.Children.Clear();
}
private void drawRedCircle()
{
Ellipse myCircle = new Ellipse();
myCircle.Stroke = Brushes.Red;
myCircle.Width = 30;
myCircle.Height = 30;
myCircle.StrokeThickness = 2;
Canvas.SetLeft(myCircle, 10);
Canvas.SetRight(myCircle, 10);
Canvas.SetBottom(myCircle, 10);
Canvas.SetTop(myCircle, 10);
myCanvas.Children.Add(myCircle);
}
private void drawBlueCircle()
{
Ellipse myCircle = new Ellipse();
myCircle.Stroke = Brushes.Blue;
myCircle.Width = 30;
myCircle.Height = 30;
myCircle.StrokeThickness = 2;
Canvas.SetLeft(myCircle, 20);
Canvas.SetRight(myCircle, 20);
Canvas.SetBottom(myCircle, 20);
Canvas.SetTop(myCircle, 20);
myCanvas.Children.Add(myCircle);
}
If you give the added circle a name, you can find it when the checkbox is unchecked and then remove it pretty easily.
private string redCircleName = "redCircle";
private string blueCircleName = "blueCircle";
private void redCB_Checked(object sender, RoutedEventArgs e)
{
drawRedCircle();
}
private void redCB_Unchecked(object sender, RoutedEventArgs e)
{
RemoveCircleByName(redCircleName);
}
private void blueCB_Checked(object sender, RoutedEventArgs e)
{
drawBlueCircle();
}
private void blueCB_Unchecked(object sender, RoutedEventArgs e)
{
RemoveCircleByName(blueCircleName);
}
private void RemoveCircleByName(string name)
{
var circle = (UIElement)LogicalTreeHelper.FindLogicalNode(myCanvas, name);
myCanvas.Children.Remove(circle);
}
private void drawRedCircle()
{
Ellipse myCircle = new Ellipse();
myCircle.Stroke = Brushes.Red;
myCircle.Width = 30;
myCircle.Height = 30;
myCircle.StrokeThickness = 2;
//Give it a name here so we can find it later
myCircle.Name = redCircleName;
Canvas.SetLeft(myCircle, 10);
Canvas.SetRight(myCircle, 10);
Canvas.SetBottom(myCircle, 10);
Canvas.SetTop(myCircle, 10);
myCanvas.Children.Add(myCircle);
}
private void drawBlueCircle()
{
Ellipse myCircle = new Ellipse();
myCircle.Stroke = Brushes.Blue;
myCircle.Width = 30;
myCircle.Height = 30;
myCircle.StrokeThickness = 2;
//Give it a name here so we can find it later
myCircle.Name = blueCircleName;
Canvas.SetLeft(myCircle, 20);
Canvas.SetRight(myCircle, 20);
Canvas.SetBottom(myCircle, 20);
Canvas.SetTop(myCircle, 20);
myCanvas.Children.Add(myCircle);
}
I think unchecked event calls later, which means that the circle created by checked event is cleared by the unchecked event.
One solution can be to move the logic of creating and clearing circles to a single method, and only registered checked events.
private void drawCircle()
{
myCanvas.Children.Clear();
if(redCB.Checked) drawRedCircle();
if(blueCB.Checked) drawBlueCircle();
}
private void redCB_Checked(object sender, RoutedEventArgs e)
{
drawCircle();
}
private void blueCB_Checked(object sender, RoutedEventArgs e)
{
drawCircle();
}
Rather then add/remove why not using the visibility ( IsVisible) of the ellipse ? Bind them to a notifying boolean property, and you bind also the checkBox to that property.
// ellipse1Visible is a notifying boolean
Binding ellipseBinding = new Binding("ellipse1Visible");
ellipseBinding .Source = ??? ; // set your binding source here (? this ?)
myEllipse.SetBinding(Ellipse.IsVisibleProperty, ellipseBinding );
myRelatedCheckBox.SetBinding(CheckBox.IsCheckedProperty, ellipseBinding)
if you want to use any number of ellipse, you have to use
an Array/List/ObservableColection of 'notifying booleans'
the syntax is :
// i is a valid index in the ellipseVisible collection of notifying boolean.
Binding ellipseBinding = new Binding("ellipseVisible[" & i & "]");
// the rest of the code is the same
you can add the checkbox in a near StackPanel/listBox/... while you add the related ellipse in the canvas.
No need to add/remove ellipse and no need to handle checked/unchecked with this solution.
Rq : if you intend to make no use of the visibility of each ellipse, you can even do it in a simpler way : no boolean, but rather you directly bind the visibility to the IsChecked property of the CheckBox :
// ... we just created myEllipse and myCheckBox
// ... and inserted them into canvas/stackPanel
Binding ellipseBinding = new Binding("IsChecked");
ellipseBinding.Source = myCheckBox;
myEllipse.SetBinding(Ellipse.IsVisibleProperty, ellipseBinding);
// and that's all

Categories

Resources