Collapsing parent Expander visibility in WPF without collapsing child Expander - c#

I am creating a WPF application for reading logs. Currently, I have Expanders inside of Expanders. When I click on an Expander, I want the scope to be the contents of that Expander. All other peer Expanders disappear (Visibility.Collapsed).
This works for the first line of Expanders. Once I expand a child Expander, this works again, but the parent Expander is still listed above it.
I'd like to collapse peers (same as before) as well as the parent, but keep the parent expanded, so that just the expanded child is showing by itself.
That was a lot of "expand" in a few sentences! But anyways ...
Here's an example of what expected output:
Initially:
Expander 1
Expander 2
Expander 3
Expander 4
Let's say I click Expander 2:
Expander 2
Child Expander 1
Child Expander 2
Child Expander 3
Child Expander 4
All others are collapsed, but still expanded. Children are displayed.
Let's say I click Child Expander 3
Child Expander 3
All others are collapsed, including the parent!
Is there a way to accomplish this?
Here's my current code (Toggling is kindof confusing at first):
private Boolean toggleLogs = false;
private Boolean subToggleLogs = false;
public LogsControl()
{
InitializeComponent();
}
private void expanderPanel_Click(object sender, RoutedEventArgs e)
{
toggleLogs = !toggleLogs;
if (toggleLogs == true)
{
setLogVisibility(expanderPanel);
} else
{
toggleLogVisibility(expanderPanel);
}
}
private void windowsServicePanel_Click(object sender, RoutedEventArgs e)
{
toggleLogs = false;
subToggleLogs = !subToggleLogs;
if (subToggleLogs == true)
{
setLogVisibility(windowsServicePanel);
}
else
{
toggleLogVisibility(windowsServicePanel);
}
}
private void javaPanel_Click(object sender, RoutedEventArgs e)
{
toggleLogs = false;
subToggleLogs = !subToggleLogs;
if (subToggleLogs == true)
{
setLogVisibility(javaPanel);
}
else
{
toggleLogVisibility(javaPanel);
}
}
public void toggleLogVisibility(StackPanel panel)
{
var childNumber = VisualTreeHelper.GetChildrenCount(panel);
for (var i = 0; i <= childNumber - 1; i++)
{
var uiElement = VisualTreeHelper.GetChild(panel, i) as Expander;
uiElement.Foreground = new SolidColorBrush(Colors.White);
if (uiElement.IsExpanded == false)
{
uiElement.Visibility = Visibility.Visible;
}
}
}
public void setLogVisibility(StackPanel panel)
{
var childNumber = VisualTreeHelper.GetChildrenCount(panel);
for (var i = 0; i <= childNumber - 1; i++)
{
var uiElement = VisualTreeHelper.GetChild(panel, i) as Expander;
if (uiElement.IsExpanded == true)
{
uiElement.Foreground = (SolidColorBrush)(new BrushConverter().ConvertFrom("#FF3399FF"));
uiElement.Visibility = Visibility.Visible;
}
else
{
uiElement.Visibility = Visibility.Collapsed;
}
}
}

If you set a controls visibility to collapsed or hidden, all child controls and content is collapsed / hidden. No way around that. You'd have to simulate the hierarchy if you want to do what you want. Instead of actually nesting the expanders, just make it look like it on the screen, but you'll have difficulty keeping all the visibility states in sync that way.

Related

Get Thumb of a Slider in UWP

I made a custom style for a slider to which I only change the shape of the Thumb.
What I would like to do is have a function which changes the size of the Thumb whenever it is triggered (maybe via a button).
The way I created my custom style is: right-click on Slider -> Edit Template -> Edit a copy
My problem is that I don't know how can I access the thumb of the slider...
I would like something like this
Thumb myThumb = mySlider.GetTemplateChild("horizontalThumb");
myThumb.Height = 50;
I saw multiple ways to do that in WPF but not in UWP.
To access the Thumb from your Slider, try this:
1: Add the Loaded event in the XAML to your slider
2: Use this function to get the child from the parent
//Get the acutal element from the parent object using the VisualTreeHelper:
//Parameters:
//parent = The object to get the element from
//childname = The name of the childobject to find
private DependencyObject GetElementFromParent(DependencyObject parent, string childname)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i =0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is FrameworkElement childframeworkelement && childframeworkelement.Name == childname)
return child;
var FindRes = GetElementFromParent(child, childname);
if (FindRes != null)
return FindRes;
}
return null;
}
3: Put this code in your slider_loaded event to get the data from the Thumb:
private void Slider_Loaded(object sender, RoutedEventArgs e)
{
var SliderThumb = GetElementFromParent(sender as DependencyObject, "HorizontalThumb"); //Make sure to put the right name for your slider layout options are: ("VerticalThumb", "HorizontalThumb")
if (SliderThumb != null)
{
if(SliderThumb is Thumb thumb)
{
//Here you can change everything you like:
thumb.Background = new SolidColorBrush(Colors.Blue);
thumb.CornerRadius = new CornerRadius(5);
thumb.Width = 10;
thumb.Height = 10;
}
else
{
//SliderThumb is not an object of type Thumb
}
}
else
{
//SliderThumb is null
}
}

AutoScroll problem when using Left Dock - C# Telerik Winforms

Here is my code:
RadScrollablePanel panel = new RadScrollablePanel() { AutoScroll = true, Dock = DockStyle.Fill};
pnlclp.PanelContainer.Controls.Add(panel);
foreach (var date in dates)
panel.Controls.Add(new ucDetails() { Dock = DockStyle.Left });
I'm adding some controls inside a RadScrollablePanel and then adding it into a PanelContainer.
Everything works great. If I add so many controls inside the RadScrollablePanel which is not visible in first look, the scroll bar will be shown as well.
But If I change the DockStyle.Left to DockStyle.Right in foreach loop, after loading the controls, it will not show the scroll bar and it is strange and I can not find any reason or solution to solve this issue.
I even try to change the RightToLeft property of RadScrollablePanel. but no success :(
Any suggestion?
Following the provided information, I have prepared a sample project to test the behaior in RadScrollablePanel.
I have logged it in our feedback portal by creating a public thread. You can track its progress, subscribe for status changes and add your comments on the following link: https://feedback.telerik.com/winforms/1453253-radscrollablepanel-missing-scrollbar-when-there-is-no-enough-space-to-display-the-content-controls
I hope this information helps.
To work around this problem of the standard Microsoft WinForms Panel, I can suggest docking all the UserControls to the Left and use an empty Panel that occupies all the available space on the left of the form, so exactly the same behavior as all UserControls are docked to the Right. When the size of the form is changed adjust the width of the empty panel. The described approach is illustrated with the code below:
public partial class Form1 : Form
{
UserControl1[] userControls;
RadScrollablePanel parentPanel;
Panel spacePanel;
public Form1()
{
InitializeComponent();
new Telerik.WinControls.RadControlSpy.RadControlSpyForm().Show();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.parentPanel = new RadScrollablePanel();
this.parentPanel.Dock = DockStyle.Fill;
this.parentPanel.BackColor = Color.Yellow;
this.Controls.Add(this.parentPanel);
this.parentPanel.AutoScroll = true;
int count = 10;
this.userControls = new UserControl1[count];
for (int i = 0; i < count; i++)
{
this.userControls[i] =
new UserControl1()
{
Dock = DockStyle.Left,
BackColor = Color.FromKnownColor((KnownColor)(i + 50))
};
this.parentPanel.Controls.Add(this.userControls[i]);
}
this.spacePanel = new Panel();
this.spacePanel.Dock = DockStyle.Left;
this.parentPanel.Controls.Add(this.spacePanel);
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
if (this.spacePanel != null)
{
int lastPanelWidth = this.parentPanel.Width;
foreach (Control control in this.parentPanel.PanelContainer.Controls)
{
if (control.Dock == DockStyle.Left && control != this.spacePanel)
{
lastPanelWidth -= control.Width;
}
}
if (lastPanelWidth < 0)
{
lastPanelWidth = 0;
}
this.spacePanel.Width = lastPanelWidth;
}
}
}

How to get child element of default scrollbar(e.g. repeatbutton, thumb, etc.) in silverlight?

I've listbox which has some collection binded & once the content size gets increased listbox gets vertical scrollviewer(default) visible. I got scrollbar from this scrollviewer as child element. But, when I'm trying to get child element (e.g. repeatbutton, thumb) from scrollbar then i get child element null. I got control hierarchy from silverlight spy. I want to get repeatbutton from vertical scrollabr(This is not custom scrollbar). For e.g.
Code :
var objVRepeatBtn = ((FrameworkElement)VisualTreeHelper.GetChild(objvScrollBar, 0)).FindName("VerticalSmallIncrease") as System.Windows.Controls.Primitives.RepeatButton;
Any approach will be accepted.
I resolved this with approach like :
private RepeatButton rb = null;
private RepeatButton rb1 = null;
private Thumb thumb = null;
public ThumbnailUserControl()
{
InitializeComponent();
//sv1 repensented as ScrollViewer's object
this.sv1.Loaded += new RoutedEventHandler(sv1_Loaded);
}
void sv1_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement fe = VisualTreeHelper.GetChild(this.sv1, 0) as FrameworkElement;
if (fe == null)
return;
var sb = fe.FindName("VerticalScrollBar") as ScrollBar;
if (sb != null)
{
thumb = (Thumb)((FrameworkElement)VisualTreeHelper.GetChild(sb, 0)).FindName("VerticalThumb");
rb1 = (RepeatButton)((FrameworkElement)VisualTreeHelper.GetChild(sb, 0)).FindName("VerticalLargeDecrease");
rb = (RepeatButton)((FrameworkElement)VisualTreeHelper.GetChild(sb, 0)).FindName("VerticalSmallIncrease");
rb.Click += new RoutedEventHandler(rb_Click);
thumb.DragCompleted += new DragCompletedEventHandler(thumb_DragCompleted);
thumb.MouseWheel += new MouseWheelEventHandler(thumb_MouseWheel);
}
}

Animate via Horizontal/Vertical offset of ScrollViewer

I need to have control whose position is directly correlated by the scroll offset of a ScrollViewer in Windows Phone 8 SDK (silverlight/wpf). Additionally I need to be able to tell what the scroll offset in a delegate of sorts so that I may change other in-app properties. Is this even possible?
I have looked all over but can not seem to find any example, nor do I seem to have a grasp of WPF/Silverlight's animation concepts enough to pick this up.
The best that I could come up with is shown below. It would appear at first to work, but unfortunately will only update when your finger is not down and the ScrollViewer is not animating, so the updates come too infrequently. I need the updates to come as a part of the animation loop, so every frame or so (60-100+ per second), I get the new scroll offset value. Is there any way to schedule the DispatchTimer in the animation loop? Or would there be some sort of better way to approach this entirely, using something like DependentProperties?
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds(16.6);
t.Tick += new EventHandler(
(object s, EventArgs ee) =>
{
// FunkBox is some ListBox
ScrollViewer sv = FindChildOfType<ScrollViewer>(FunkBox);
if (sv == null)
{
// TOffset is some TextBlock
TOffset.Text = "dur...";
}
else
{
TOffset.Text = String.Format("dur {0}", sv.HorizontalOffset);
}
});
t.Start();
}
static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue();
for (int i = System.Windows.Media.VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
{
var child = System.Windows.Media.VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
This took me a while to figure out too. Here's how you do it:
Ensure that your ScrollViewer has ManipulationMode set to 'Control'
Walk through the visual tree to find the Vertical Scrollbar child of the ScrollViewer.
Hook into its ValueChanged event.
So, you XAML would be:
<ScrollViewer x:Name="mainScrollViewer" ManipulationMode="Control">
....
</ScrollViewer>
And your code behind:
public MainPage()
{
InitializeComponent();
this.Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
ScrollBar verticalBar;
verticalBar = ((FrameworkElement)VisualTreeHelper.GetChild(mainScrollViewer, 0)).FindName("VerticalScrollBar") as ScrollBar;
verticalBar.ValueChanged += verticalBar_ValueChanged;
}
void verticalBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
double newVerticalOffset = e.NewValue;
// Set the offset of your other control here, using newVerticalOffset
}
or, for a ListBox, you would need to get the ScrollViewer from inside the Listbox using code such as:
ScrollViewer mainScrollViewer = GetVisualChild<ScrollViewer>(yourListBoxControl);
// ...then use the code above
public T GetVisualChild<T>(UIElement parent) where T : UIElement
{
T child = null; // default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
UIElement element = (UIElement)VisualTreeHelper.GetChild(parent, i);
child = element as T;
if (child == null)
child = GetVisualChild<T>(element);
if (child != null)
break;
}
return child;
}
You might also need to set the manipulation mode of the ScrollViewer in your constructor:
ScrollViewer mainScrollViewer = GetVisualChild<ScrollViewer>(lstTest);
sv.ManipulationMode = ManipulationMode.Control;

Manage windows inside a panel with a 'Windows'-like menu

I've a panel.
I add WinForms inside it. The WinForms added have the TopLevel and Visible properties set to FALSE and TRUE.
I can do a panel.SetChildIndex(WinForm1,0) to bring WinForm1 to front.
What I've not managed to do is keep a track of the actual ChildIndex of the panel.
The idea is to have buttons that opens forms inside the panel, and that when the panel opens a new button is added in a Windows menu.
Something like when many files are open on a VS Project, you can go to Window menu and select one. Also, if you change the active page by clicking the page, the Window menu auto-updates and checks the actual active page.
I want to do this, but with a panel container. I've managed to get done everithing, but not the the Window menu auto-updates and checks the actual active page part.
Isn't there an event fired when BringToFront() or SetChildIndex(form, index) are called? Any event when I click another form that's inside the panel and it becomes the "active one"? Or some property of the panel that I can keep track of that changes when active form changes?
It is taken from here
When Control's ZOrder is chaged layout operation is always performed
in control's container control.
When I subscribed to container's Layout event and called
BringToFront() it showed me Control that changed its
ZOrder(LayoutEventArgs.AffectedControl) and changed property
(LayoutEventArgs.AffectedProperty).
Found that when a form inside a panel is closed, the Controls property of the panel gets reindexed, where the index zero is the form that gets the new focus. Now that I've a way to check the form that's in front when I close another one, windows administration in panels is done.
Going to put the source code, maybe it can help someone :)
Please note that I'm using a RadRibbonForm, a standard panel, and RadForms inside the panel. Rad's are from Telerik. Some things should change to make this work on standardWinForms, but the changes are minimal.
Also, I'm not using a menu that shows the forms, I'm using RadButtonElement's in a page of the ribbon menu instead.
AddRadFormWindow must be called to put a window and manage it automatically.
Example of adding a window:
AddRadFormWindow(typeof (MyRadForm))
Now, the source. It must be inside the code of the RadRibbonForm's class.
public static class ExtensionsRadForm
{
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, uint msg);
public static void Deminimize(this RadForm form)
{
if (form.WindowState == FormWindowState.Minimized)
ShowWindow(form.Handle, 9);
}
}
private void RefreshButtonsChecks(string windowName)
{
if (windowName != null)
{
principalPanel.Controls[windowName].BringToFront();
}
if (principalPanel.Controls.Count > 0)
{
if (principalPanel.Controls.Cast<RadForm>().Any(radForm => radForm.WindowState != FormWindowState.Minimized))
{
foreach (RadItem item in radRibbonBarGroupOpenWindows.Items)
{
var buttonBorder = ((RadButtonElement) item).BorderElement;
if (item.Name == panelPrincipal.Controls[0].Name + "Button")
{
buttonBorder.ForeColor = Color.LimeGreen;
buttonBorder.BottomColor = Color.LimeGreen;
buttonBorder.TopColor = Color.LimeGreen;
buttonBorder.LeftColor = Color.LimeGreen;
buttonBorder.RightColor = Color.LimeGreen;
principalPanel.Controls[0].Focus();
}
else
{
buttonBorder.ForeColor = Color.Transparent;
buttonBorder.BottomColor = Color.Transparent;
buttonBorder.TopColor = Color.Transparent;
buttonBorder.LeftColor = Color.Transparent;
buttonBorder.RightColor = Color.Transparent;
}
}
}
else
{
foreach (RadItem item in radRibbonBarGroupAbiertas.Items)
{
var buttonBorder = ((RadButtonElement)item).BorderElement;
buttonBorder.ForeColor = Color.Transparent;
buttonBorder.BottomColor = Color.Transparent;
buttonBorder.TopColor = Color.Transparent;
buttonBorder.LeftColor = Color.Transparent;
buttonBorder.RightColor = Color.Transparent;
}
}
}
}
private void PrincipalPanelLayout(object sender, LayoutEventArgs e)
{
RefreshButtonsChecks(null);
}
private void RadButtonElementCloseAllWindowsClick(object sender, EventArgs e)
{
int limitButtons = radRibbonBarGroupOpenWindows.Items.Count;
for (int index = 0; index < limitButtons; index++)
{
RadItem radItem = radRibbonBarGroupOpenWindows.Items[0];
radItem.Dispose();
}
int limitControls = principalPanel.Controls.Count;
for (int index = 0; index < limitControls; index++)
{
Control control = principalPanel.Controls[0];
control.Dispose();
}
Update();
GC.Collect();
}
private void AddRadFormWindow(Type windowToAdd)
{
if (!principalPanel.Controls.ContainsKey(windowToAdd.Name))
{
var window = (RadForm) Activator.CreateInstance(windowToAdd);
window.TopLevel = false;
window.Visible = true;
window.FormClosing += (method, args) =>
{
radRibbonBarGroupOpenWindows.Items[window.Name + "Button"].Dispose();
GC.Collect();
};
window.Enter += (method, args) => RefreshButtonsChecks(window.Name);
var closeMenuItem = new RadMenuItem("Close");
closeMenuItem.MouseDown += (method, args) =>
{
panelPrincipal.Controls[window.Name].Dispose();
radRibbonBarGroupOpenWindows.Items[window.Name + "Button"].Dispose();
};
var contextMenu = new RadContextMenu();
contextMenu.Items.Add(closeMenuItem);
var button = new RadButtonElement(window.Text) {Name = window.Name + "Button"};
button.MouseDown += (method, args) =>
{
switch (args.Button)
{
case MouseButtons.Left:
if (((RadForm) principalPanel.Controls[window.Name]).WindowState ==
FormWindowState.Minimized)
((RadForm) principalPanel.Controls[window.Name]).Deminimize();
principalPanel.Controls[window.Name].BringToFront();
principalPanel.Controls[window.Name].Focus();
break;
case MouseButtons.Right:
contextMenu.Show(MousePosition);
break;
}
};
radRibbonBarGroupOpenWindows.Items.Add(button);
principalPanel.Controls.Add(window);
principalPanel.Controls[window.Name].BringToFront();
principalPanel.Controls[window.Name].Focus();
}
principalPanel.Controls[windowToAdd.Name].BringToFront();
principalPanel.Controls[windowToAdd.Name].Focus();
Update();
GC.Collect();
}
public Constructor()
{
panelPrincipal.Layout += PanelPrincipalLayout;
}

Categories