In WPF, create expander based on combobox input - c#

I'm using this example to solve a larger problem, but since I'm new to wpf and C#, I've got to start somewhere right? Alright, so what I want to do is create a new expander based on combobox input. My current code for this is very simple.
MainWindow.xaml
<!-- MainWindow.xaml -->
<Grid>
<StackPanel>
<ComboBox IsEditable="True" IsReadOnly="True" Text="Default Text" HorizontalAlignment="Left" Width="260" Height="30">
<ComboBoxItem PreviewMouseLeftButtonDown="method1" Name="method1>1</ComboBoxItem>
<ComboBoxItem PreviewMouseLeftButtonDown="method2" Name="method1>2</ComboBoxItem>
<ComboBoxItem PreviewMouseLeftButtonDown="method3" Name="method1>3</ComboBoxItem>
<ComboBoxItem PreviewMouseLeftButtonDown="method4" Name="method1>4</ComboBoxItem>
</ComboBox>
</StackPanel>
</Grid>
MainWindow.xaml.cs
//MainWindow.xaml.cs
Public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void method1(object sender, MouseEventArgs e)
{
MessageBox.Show("method1");
}
private void method2(object sender, MouseEventArgs e)
{
MessageBox.Show("method2");
}
private void method3(object sender, MouseEventArgs e)
{
MessageBox.Show("method3");
}
private void method4(object sender, MouseEventArgs e)
{
MessageBox.Show("method4");
}
//public class dynamicExpanderCreation
//{
//Here's where I'm assuming the class for dynamic creation should go.
//}
Instead of each one calling a method, I'd like to have them create an expander that is created based on the selection of the combobox. For example, if you were to select 3, then an expander appears to the left, labeled 3. Then if you select 1, an expander appears below the #3 expander, labeled 1.
I'm guessing you create a class in the MainWindow.xaml.cs file, and create a new instance of the expander per selection of the combobox. I've found examples that are a little too complicated for me to follow based on my very simple task. The examples I've looked at are here, here, and here
I'm not saying these examples are bad, just that at my experience level, I can't get any of them to work. Any help is appreciated.

In WPF you need to put any of the panels like
stackpanel/wrappanel/dockpanel/Grid
<ComboBox Name="combobox" IsEditable="False" SelectionChanged="ComboBox_SelectionChanged" Text="Default Text" HorizontalAlignment="Left" Width="260" Height="30">
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
<ComboBoxItem>4</ComboBoxItem>
</ComboBox>
<StackPanel Name="dock">
</StackPanel>
And in the Codebehind
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var itemIndex = combobox.SelectedItem;
Expander expander = new Expander();
dock.Children.Add(expander);
}
Where dock is the name of your panel
Hope this helps.

Related

Access Datacontext from binded user control

I have build a dynamic UserControl from an ObservableCollection as follows...
public static ObservableCollection<Model.Model.ControleData> ListControleMachine = new ObservableCollection<Model.Model.ControleData>();
public Genkai(string Autorisation) {
InitializeComponent();
DataContext = this;
icTodoList.ItemsSource = ListControleMachine;
Model.Model.ControleData v = new Model.Model.ControleData();
v.ComputerName = "M57095";
v.ImportSource = "LOAD";
ListControleMachine.Add(v);
}
XAML
<ItemsControl x:Name="icTodoList" ItemsSource="{Binding ListControleMachine}" >
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ControlMachineII}">
<local:ControlMachineII />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But how can I access the DataContext from C# code?
For example say I want to delete the UserControl with a close button in itself, I need at least access ControleData.ComputerName value then remove it from Mainform.ListControleMachine.
I can't find the best practice for achieve this and play with my data in UserControl code.
The remove button code is like this i think (with hard coded value)
Genkai.ListControleMachine.Remove(Genkai.ListControleMachine.Where(X => X.ComputerName == "M57095").Single());
i finaly found that my DataContext was not yet initialized at start that why i got error so i had to wait for the datacontext first: here code for correction
public ControlMachineII()
{
InitializeComponent();
DataContextChanged += new DependencyPropertyChangedEventHandler(ControlMachineII_DataContextChanged);
}
private void ControlMachineII_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
string compname = (this.DataContext as Model.Model.ControleData).ComputerName;
Console.WriteLine("DataContext initialized computername :" +compname);
}
I saw you posted same question today with some more data. I'm going to present the solution using that data.
Solution 1 :
Use Tag property of button like below:
<Button Content="Close this UC" HorizontalAlignment="Left" Margin="414,22,0,0"
VerticalAlignment="Top" Width="119" Click="Button_Click" Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
Event handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
List<object> list = (button.Tag as ItemsControl).ItemsSource.OfType<TodoItem>().ToList<object>();
list.Remove(button.DataContext);
(button.Tag as ItemsControl).ItemsSource = list;
}
Solution 2:
More elegant solution:
Create this Style in your MainWindow:
<Window.Resources>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="Button_Click"/>
</Style>
</Window.Resources>
So now the Handler of any Button Click event in any MainWindow's descendant Button Control is in the MainWindow.xaml.cs.
Then place the handler method in MainWindow.xaml.cs and change the handler like below:
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
items.Remove(button.DataContext as TodoItem);
icTodoList.ItemsSource = null;
icTodoList.ItemsSource = items;
}

Saving user color settings of a clicked Button in WPF

I have a little problem with saving some properties of my Buttons. The Buttons are small and with a variety of colors. When i press one button, some specified colors are changing... and i want to save them for the next start up. The textbox values i can save them but this ...i can't.
Code:
public MainWindow()
{
InitializeComponent();
//blueColor.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
//this.Property = Properties.Settings.Default.userColor;
}
private void blueColor_Click(object sender, RoutedEventArgs e)
{
var bc = new BrushConverter();
Main.Background = (Brush)bc.ConvertFrom("#FF007CE4");
startButton.Foreground = (Brush)bc.ConvertFrom("#FF007CE4");
closeButton.Foreground = (Brush)bc.ConvertFrom("#FF007CE4");
Properties.Settings.Default.userColor = true;
Properties.Settings.Default.Save();
}
private void purpleColor_Click(object sender, RoutedEventArgs e)
{
var bc = new BrushConverter();
Main.Background = (Brush)bc.ConvertFrom("#FF8701B9");
startButton.Foreground = (Brush)bc.ConvertFrom("#FF8701B9");
closeButton.Foreground = (Brush)bc.ConvertFrom("#FF8701B9");
}
I think I need the last clicked Button to be saved because I have allot of colors and maybe the .RaiseEvent can help here.
This is how it looks like:
Those 3 little buttons:
white
blue
red
are for changing the look of the program. At every start, the default is back.
You can store the color as a simple string and TypeConverter automatically converts it to type Brush. Below is an example.
Binding default value from XAML:
xmlns:properties="clr-namespace:WorkWithSettings.Properties"
<Button Width="100" Height="30"
Background="{Binding Source={x:Static properties:Settings.Default}, Path=Setting, Mode=TwoWay}" />
Set value from code:
private void Button_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.Setting = "#FF007CE4";
}
Note: Setting - this is just the type of String.
More information you can see here:
TypeConverters and XAML
Edit:
Below I'll show you an example, that I hope will help you.
So, go into the settings of the project: Project -> Properties -> Parameters. This opens a window of approximately:
Here we have a property ButtonColor, defined in the settings. For example, I took the Button, which changes the background, depending on the color of the pressed button.
In order to property Background the synchronize with settings to do, so:
<Button Width="100" Height="30"
Content="TestButton"
Background="{Binding Source={x:Static properties:Settings.Default}, Path=ButtonColor, Mode=TwoWay}" />
The default background color of white. Now, to set the background color at the button, we change the parameter settings, like this:
private void Blue_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "Blue";
}
To save changes to the settings, you need to call a method Save():
private void Save_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.Save();
}
Now, the next time you start the program, the color will be the one that was set last.
Full example
XAML
<Window x:Class="WorkWithSettings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:WorkWithSettings.Properties"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock Width="100" Height="30" Text="{Binding Source={x:Static properties:Settings.Default}, Path=ButtonColor, Mode=TwoWay}" Margin="0,60,0,0" />
<Button Width="100" Height="30" Content="TestButton" Background="{Binding Source={x:Static properties:Settings.Default}, Path=ButtonColor, Mode=TwoWay}" />
<WrapPanel>
<Button Name="Blue" Width="100" Height="30" Content="BlueColor" VerticalAlignment="Top" Click="Blue_Click" />
<Button Name="Red" Width="100" Height="30" Content="RedColor" VerticalAlignment="Top" Click="Red_Click" />
<Button Name="White" Width="100" Height="30" Content="WhiteColor" VerticalAlignment="Top" Click="White_Click" />
</WrapPanel>
<Button Name="Save" Width="60" Height="30" Content="Save" VerticalAlignment="Top" HorizontalAlignment="Right" Click="Save_Click" />
</Grid>
</Window>
Code behind
namespace WorkWithSettings
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void White_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "White";
}
private void Blue_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "Blue";
}
private void Red_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "Red";
}
private void Save_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.Save();
}
}
}
Output
You probably need to create items in the Settings tab of your project that store the information about the color. I would recommend storing the hex strings. Then, on MainForm_Load retrieve those values.
Make sure to also put the settings in the User scope, or else they will reset each time they close the application.

ComboBox default value?

I'm writing a Windows Store app and I need a ComboBox to have its default value. I also would like to know which item from the list the user selects but I can't find out how to do it. I tried different properties but with no results. Any ideas about doing this?
The code I have for create the ComboBox is:
<ComboBox x:Name="cboxelemento" Width="350" ItemsSource="{Binding}"
SelectionChanged="cboxelemento_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="lnombre" Text="{Binding Nombre}" FontSize="24"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.DataContext>
<Clases:Datos/>
</ComboBox.DataContext>
</ComboBox>
private void cmbox_SelectedValueChanged(object sender, EventArgs e)
{
var val = cmbox.SelectedValue;
//or
//cmbox.Selectedindex;
}
you can access the value like this
First you must use the SelectedValueChanged Event.
private void cmbox_SelectedValueChanged(object sender, EventArgs e)
{
if (cmbox.Focused)
{
//do
}
}
The property Focused goes to true when you have clicked on the ComboBox.

Give focus to a component inside an expander

I have this requirement where I need to focus the first element inside the expander when the user press tab.
Currently (default behavior) the focus goes to the expander, I've tried to focus the first element of the expander by creating a focus event handler in the expander like this:
private void ExpanderGotFocus(object sender, RoutedEventArgs e)
{
var expander = (Expander) sender;
if (!expander.IsExpanded)
{
expander.IsExpanded = true;
this._someText.Focus();
}
}
Which doesn't work.
I've also tried to give the focus the the next element:
var tRequest = new TraversalRequest(FocusNavigationDirection.Next);
var keyboardFocus = Keyboard.FocusedElement as UIElement;
keyboardFocus.MoveFocus(tRequest);
But only works the second time ( when the expander has been at least opened once )
I've tried to put this in a thread and some other crazy ideas.
How can I give focus to the first element inside an expander? ( the first time the expander is closed )
I tried several ways and none of them worked, basically the problem is the TextBox is still rendering when the expander is expanding ( to early ).
So instead what I've found is to add the IsVisibleChanged event to the textbox so when the expander finished the textbox become visible and request the focus
XAML
<Expander GotFocus="ExpanderGotFocus">
<Expander.Header>
<TextBlock Text="{x:Static Client:Strings.XYZ}" />
</Expander.Header>
<Expander.Content>
<StackPanel>
<TextBox IsVisibleChanged="ControlIsVisibleChanged" Name="txtBox" />
</StackPanel>
</Expander.Content>
</Expander>
Code behind
private void ExpanderGotFocus(object sender, RoutedEventArgs e)
{
var expander = (Expander) sender;
if (!expander.IsExpanded )
{
expander.IsExpanded = true;
}
}
private void ControlIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Keyboard.Focus((IInputElement)sender);
}
Check with the following,
XAML code:
<StackPanel>
<Expander Header="Expander"
Name="expander"
Collapsed="OnCollapsed"
IsExpanded="True" >
<StackPanel>
<TextBox Text="Text1" Name="textBox1" />
<TextBox Text="Text2" Name="textBox2" />
<TextBox Text="Text3" Name="textBox3" />
</StackPanel>
</Expander>
<TextBox Text="Text4" Name="textBox4" />
</StackPanel>
in the code behind:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.Loaded += delegate
{
textBox2.Focus();
};
}
private void OnCollapsed(object sender, RoutedEventArgs e)
{
var element = Keyboard.FocusedElement;
if (element != null)
{
//now is the ToggleButton inside the Expander get keyboard focus
MessageBox.Show(element.GetType().ToString());
}
//move focus
Keyboard.Focus(textBox4);
}
}

Calling a base-class method from Silverlight XAML

As part of learning Silverlight, I'm trying to create a base UserControl to use as the starting point for my inherited controls.
It's very simple, it merely defines some callback methods:
public class ClickableUserControl : UserControl
{
private Control _superParent;
public ClickableUserControl()
{
}
public ClickableUserControl(Control superParent)
{
_superParent = superParent;
this.MouseEnter += new MouseEventHandler(PostfixedLayoutItem_MouseEnter);
this.MouseLeave += new MouseEventHandler(PostfixedLayoutItem_MouseLeave);
this.MouseLeftButtonDown += new MouseButtonEventHandler(PostfixedLayoutItem_MouseLeftButtonDown);
}
public virtual void PostfixedLayoutItem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var elements = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), this);
if (elements.Any(elm => elm is ClickToEditTextBox))
{
e.Handled = false;
}
}
public void PostfixedLayoutItem_MouseLeave(object sender, MouseEventArgs e)
{
this.Cursor = Cursors.Arrow;
}
public void PostfixedLayoutItem_MouseEnter(object sender, MouseEventArgs e)
{
this.Cursor = Cursors.Hand;
}
public void ClickToEditTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter || e.Key == Key.Escape)
{
VisualStateManager.GoToState((Control)sender, "NotEdit", false);
_superParent.Focus();
}
}
}
Please note the ClickToEditTextBox_KeyDown() method which is the problem!
Now, I have an inherited control that looks as follows (CheckboxLayoutItem.xaml):
<local:ClickableUserControl x:Class="OpusFormBuilder.LayoutItems.CheckboxLayoutItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:OpusFormBuilder.LayoutItems"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" x:Name="LayoutItem">
<StackPanel Name="stackPanel1" Orientation="Horizontal">
<lc:LayoutItem Label="layoutItem" Name="layoutItem">
<lc:LayoutItem.LabelTemplate>
<DataTemplate>
<Self:ClickToEditTextBox KeyDown="ClickToEditTextBox_KeyDown" Text="{Binding Label, Mode=TwoWay, ElementName=layoutItem}" MaxWidth="150" MinWidth="150" TextWrapping="Wrap" MaxHeight="200" VerticalAlignment="Top" />
</DataTemplate>
</lc:LayoutItem.LabelTemplate>
<dxe:CheckEdit Name="InnerCheckbox" Grid.ColumnSpan="2" Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Stretch" IsEnabled="False" />
</lc:LayoutItem>
<Self:ClickToEditTextBox KeyDown="ClickToEditTextBox_KeyDown" x:Name="Description" MaxWidth="150" MaxHeight="200" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Right" />
</StackPanel>
(Note - I have removed some namespace declarations for easier reading)
Note the following line:
<Self:ClickToEditTextBox KeyDown="ClickToEditTextBox_KeyDown" Text="{Binding Label, Mode=TwoWay, ElementName=layoutItem}" MaxWidth="150" MinWidth="150" TextWrapping="Wrap" MaxHeight="200" VerticalAlignment="Top" />
in which I set the KeyDown-event on a ClickToEditTextBox (the self namespace is defined, and correctly so).
Now, in the code behind (CheckboxLayoutItem.xaml.cs) in the constructor the call to InitializeComponent() fails with the error: Failed to assign to property 'System.Windows.UIElement.KeyDown'. [Line: 17 Position: 42]
I can't debug into InitializeComponent, however, but I can't see what could possibly be the issue from this error, other than the KeyDown events in the XAML.
Now, here is my question - how come I (seemingly) cannot reference a method defined in my base-class!? Previously I had the method in the CheckboxLayoutItem.xaml.cs method itself, but as some other controls needed some of the same functionality, it seemd a better option to put it in a base class.
Cheers!
I know this doesn't really answer your question, but you might want to look at Template (Custom Controls) controls. UserControl really isn't the best solution for what you're trying to do here.
UserControls are best for situations where you're building a one-off control that you don't intend in inheriting from.

Categories