Accordion Item not in correct Visual State - silverlight

I need to use an Accordion to display some totals on a LOB application we are building.
If I place the Accordion in XAML all works fine and the state of the icon (>) is correct and pointing to the right. On Mouse entering the AccordionItem we do not have a visual state change.
If I dynamically add AccordionItems on a Button Click (to simulate async data call returning) the state of the icon is not the same and on MouseEnter it "corrects" itself by executing a visual state change. *You may need to click "Add 3 Accordion Items" twice.
If I dynamically add an Accordion on a Button click with AccordionItems it works fine. Below is my sample Application.
So what do I need to do to get the Accordion to add AcordionItems at runtime and be in the correct state as per when using XAML?
XAML
<Grid x:Name="LayoutRoot" Background="Black" >
<StackPanel x:Name="TheStackPanel">
<Button Content="Create Accordion" Click="CreateAccordionItems"></Button>
<Button Content="Add 3 Accordion Items" Click="AddAccordionItems"></Button>
<Grid Background="Pink">
<layoutToolkit:Accordion SelectionMode="ZeroOrMore" x:Name="TestAccordion" Margin="10,10,10,10" HorizontalAlignment="Stretch" >
<layoutToolkit:AccordionItem Content="Content - 1" Header="Header - 1">
</layoutToolkit:AccordionItem>
<layoutToolkit:AccordionItem Content="Content - 2" Header="Header - 2">
</layoutToolkit:AccordionItem>
<layoutToolkit:AccordionItem Content="Content - 3" Header="Header - 3">
</layoutToolkit:AccordionItem>
</layoutToolkit:Accordion>
</Grid>
</StackPanel>
public partial class MainPage : UserControl
{
private int count = 0;
public MainPage()
{
// Required to initialize variables
InitializeComponent();
//TestAccordion.ExpandDirection = ExpandDirection.Down;
}
private void AddAccordionItems( object sender, RoutedEventArgs e )
{
AddToAccordion( 3, TestAccordion );
}
private void AddToAccordion( int size, Accordion _Accordion )
{
for( int i = 0; i < size; i++ )
{
AccordionItem accordionItem = new AccordionItem( );
accordionItem.Header = "Item " + count.ToString( );
count++;
_Accordion.Items.Add( accordionItem );
Grid aGrid = new Grid( );
TextBlock tb = new TextBlock( );
tb.Text = accordionItem.Header as string;
aGrid.Children.Add( tb );
accordionItem.Content = aGrid;
//accordionItem.IsEnabled = true;
accordionItem.IsSelected = true;
}
}
private void CreateAccordionItems( object sender, RoutedEventArgs e )
{
Accordion accordion = new Accordion( );
accordion.HorizontalContentAlignment = HorizontalAlignment.Stretch;
TheStackPanel.Children.Add( accordion );
AddToAccordion( 10, accordion );
}
}

If you take a look at the source code for the Accordian control you'll see that it uses the InteractionHelper.UpdateVisualState to set its correct state after events.
public void UpdateVisualStateBase(bool useTransitions)
{
if (!this.Control.IsEnabled)
{
VisualStates.GoToState(this.Control, useTransitions, new string[] { "Disabled", "Normal" });
}
else if (this.IsReadOnly)
{
VisualStates.GoToState(this.Control, useTransitions, new string[] { "ReadOnly", "Normal" });
}
else if (this.IsPressed)
{
VisualStates.GoToState(this.Control, useTransitions, new string[] { "Pressed", "MouseOver", "Normal" });
}
else if (this.IsMouseOver)
{
VisualStates.GoToState(this.Control, useTransitions, new string[] { "MouseOver", "Normal" });
}
else
{
VisualStates.GoToState(this.Control, useTransitions, new string[] { "Normal" });
}
if (this.IsFocused)
{
VisualStates.GoToState(this.Control, useTransitions, new string[] { "Focused", "Unfocused" });
}
else
{
VisualStates.GoToState(this.Control, useTransitions, new string[] { "Unfocused" });
}
}
Since the method is marked internal on the Accordian control and the InteractionHelper is a private variable, your best bet is to figure out which of the states you're adding the control in and then tell the control to go to that state (without a transition) before addig it to the visual tree. This is why the MouseOver is "fixing" it.

Call
TestAccordion.UpdateLayout();
after adding the items... may be

Can you bind the accordian Items to an ObservableCollection?

Related

Get owner of context menu on Button

Title just about says it all.I have a lot of buttons, all of them have the same context menu, I want to determine from the click event which button was rgiht clicked to get there.
This code does not work, placementTarget is null:
private void mi_Click(object sender, RoutedEventArgs e)
{
Button contextMenuEzen = null;
MenuItem mnu = sender as MenuItem;
if (mnu != null)
{
ContextMenu ize =(ContextMenu)mnu.Parent;
contextMenuEzen = ize.PlacementTarget as Button;
}
}
Please help me!
For me, this example works:
XAML
<Window.Resources>
<!-- For all MenuItems set the handler -->
<Style TargetType="{x:Type MenuItem}">
<EventSetter Event="Click" Handler="MenuItem_Click" />
</Style>
<!-- One ContextMenu for all buttons (resource) -->
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="Click this" />
<MenuItem Header="Two" />
<MenuItem Header="Three" />
</ContextMenu>
</Window.Resources>
<Grid>
<Button x:Name="MyButton1" Width="100" Height="30" Content="MyButton1" ContextMenu="{StaticResource MyContextMenu}" />
<Button x:Name="MyButton2" Margin="0,110,0,0" Width="100" Height="30" Content="MyButton2" ContextMenu="{StaticResource MyContextMenu}" />
</Grid>
Code behind
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem mnu = sender as MenuItem;
Button MyButton = null;
if (mnu != null)
{
ContextMenu MyContextMenu = (ContextMenu)mnu.Parent;
MyButton = MyContextMenu.PlacementTarget as Button;
}
MessageBox.Show(MyButton.Content.ToString());
}
RoutedEvents don't work exactly like regular events - the signature of the handler is EventHandler(object sender, RoutedEventArgs e). The RoutedEventArgs has a property called OriginalSource that represents the element in your UI that was actually clicked. In contrast, the sender parameter will always be the object on which the event handler is registered.
use the ContextMenuService to get the placement target as in the following example:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += (sender, args) => {
RoutedEventHandler clickHandler = delegate(object o, RoutedEventArgs eventArgs) {
var mi = (MenuItem) o;
var contextMenu = (ContextMenu) mi.Parent;
var pTarget = ContextMenuService.GetPlacementTarget(contextMenu) as Button;
// just to make sure
if (pTarget == null) {
return;
}
string message = "You clicked on the button " + pTarget.Content;
MessageBox.Show(message);
};
// create a single instance of the ContextMenu
var cm = new ContextMenu();
for (int i = 0; i < 10; i++) {
var mi = new MenuItem {Header = "Item " + i};
mi.Click += clickHandler;
cm.Items.Add(mi);
}
// create a set of buttons and assign them to the RootVisual(StackPanel)
for (int i = 0; i < 5; i++) {
var button = new Button {Content = "Button " + i, ContextMenu = cm};
this.RootVisual.Children.Add(button);
}
};
}
}

How to do an update my TreeView in wpf?

Good evening.
I started studying the technology - wpf in c#
Now I stopped at the section - work with treeview
I want to add a new node in my treeview in my task.
I have a form and button on this form:
...
<Button Name="btn1" Click="ShowForWriteAdrBookName" Content="TestBtn"/>
...
When you run the program on my form is loaded UserControl, which have treeview:
...
<TreeView Name="treeView1"></TreeView>
...
When I press the
...
<Grid>
<Label Name="Label1" Content="Имя адресной книги:" Margin="0,20,0,0"></Label>
<TextBox Name="TextBox1" Width="200" Height="30" Margin="90,-40,0,0"></TextBox>
<Button Content="Ок" Width="80" Height="30" Margin="0,50,0,0" Click="GetAdrBookName"></Button>
<Button Content="Отмена" Width="80" Height="30" Margin="200,50,0,0" Click="MyExit"></Button>
</Grid>
...
After entering text in the TextBox I click OK. Function is called:
...
public void GetAdrBookName(object sender, RoutedEventArgs e)
{
if (String.Compare(TextBox1.Text.ToString(), "") != 0)
{
adrName = TextBox1.Text.ToString().Trim();
this.Close();
CreateAdrBook(adrName);
}
else
{
...
}
}
...
In this function, I read information from textbox. And then call another function:
...
public void CreateAdrBook(string adrNameFromTextBox1)
{
mylist.Add(adrNameFromTextBox1);
int s = mylist.Count;
TreeView();
}
...
In this function, I add getting information from my textbox to my collection:
...
ObservableCollection<string> mylist = new ObservableCollection<string>()
{
"A",
"B",
"C"
};
...
After I added more information in my collection, I add this information to my TreeView in function:
...
void TreeView()
{
foreach (var drive in mylist)
{
TreeViewItem item = new TreeViewItem();
item.Tag = drive;
item.Header = drive.ToString();
item.Items.Add("*");
treeView1.Items.Add(item);
treeView1.Items.Refresh();
treeView1.UpdateLayout();
}
}
...
Question:
How to do an update my TreeView after adding in this TreeView a new element?
Thank you!

Delete items from ListBox in WPF?

I am trying to delete items from listbox which is data bound.
Here is the screenshot how listbox look like.
This is the code which adds items in lists.
public class Task
{
public string Taskname { get; set; }
public Task(string taskname)
{
this.Taskname = taskname;
}
}
public void GetTask()
{
taskList = new List<Task>
{
new Task("Task1"),
new Task("Task2"),
new Task("Task3"),
new Task("Task4")
};
lstBxTask.ItemsSource = taskList;
}
This is the Xaml code,
<ListBox x:Name="lstBxTask" Style="{StaticResource ListBoxItems}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Taskname}" Style="{StaticResource TextInListBox}"/>
<Button Name="btnDelete" Style="{StaticResource DeleteButton}" Click="btnDelete_Click">
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Whenever item in a listbox is selected, delete (x) button is displayed. When clicked it should delete that item from the listbox. Can anyone tell me how can I do this?
ok this is what i did. Observablecollection worked like charm.
private ObservableCollection<Task> taskList;
public void GetTask()
{
taskList = new ObservableCollection<Task>
{
new Task("Task1"),
new Task("Task2"),
new Task("Task3"),
new Task("Task4")
};
lstBxTask.ItemsSource = taskList;
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button != null)
{
var task = button.DataContext as Task;
((ObservableCollection<Task>) lstBxTask.ItemsSource).Remove(task);
}
else
{
return;
}
}
Try using an ObservableCollection<T> instead of a simple List<T>.
The ObservableCollection<T> will notify the WPF-binding-system whenever its content has changed. Therefore, you will only have to remove the item from the list and the UI will be updated.

WPF: Create a dialog / prompt

I need to create a Dialog / Prompt including TextBox for user input. My problem is, how to get the text after having confirmed the dialog? Usually I would make a class for this which would save the text in a property. However I want do design the Dialog using XAML. So I would somehow have to extent the XAML Code to save the content of the TextBox in a property - but I guess that's not possible with pure XAML. What would be the best way to realize what I'd like to do? How to build a dialog which can be defined from XAML but can still somehow return the input? Thanks for any hint!
The "responsible" answer would be for me to suggest building a ViewModel for the dialog and use two-way databinding on the TextBox so that the ViewModel had some "ResponseText" property or what not. This is easy enough to do but probably overkill.
The pragmatic answer would be to just give your text box an x:Name so that it becomes a member and expose the text as a property in your code behind class like so:
<!-- Incredibly simplified XAML -->
<Window x:Class="MyDialog">
<StackPanel>
<TextBlock Text="Enter some text" />
<TextBox x:Name="ResponseTextBox" />
<Button Content="OK" Click="OKButton_Click" />
</StackPanel>
</Window>
Then in your code behind...
partial class MyDialog : Window {
public MyDialog() {
InitializeComponent();
}
public string ResponseText {
get { return ResponseTextBox.Text; }
set { ResponseTextBox.Text = value; }
}
private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
DialogResult = true;
}
}
Then to use it...
var dialog = new MyDialog();
if (dialog.ShowDialog() == true) {
MessageBox.Show("You said: " + dialog.ResponseText);
}
Edit: Can be installed with nuget https://www.nuget.org/packages/PromptDialog/
I just add a static method to call it like a MessageBox:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="utils.PromptDialog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight"
MinWidth="300"
MinHeight="100"
WindowStyle="SingleBorderWindow"
ResizeMode="CanMinimize">
<StackPanel Margin="5">
<TextBlock Name="txtQuestion" Margin="5"/>
<TextBox Name="txtResponse" Margin="5"/>
<PasswordBox Name="txtPasswordResponse" />
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
<Button Content="_Ok" IsDefault="True" Margin="5" Name="btnOk" Click="btnOk_Click" />
<Button Content="_Cancel" IsCancel="True" Margin="5" Name="btnCancel" Click="btnCancel_Click" />
</StackPanel>
</StackPanel>
</Window>
And the code behind:
public partial class PromptDialog : Window
{
public enum InputType
{
Text,
Password
}
private InputType _inputType = InputType.Text;
public PromptDialog(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(PromptDialog_Loaded);
txtQuestion.Text = question;
Title = title;
txtResponse.Text = defaultValue;
_inputType = inputType;
if (_inputType == InputType.Password)
txtResponse.Visibility = Visibility.Collapsed;
else
txtPasswordResponse.Visibility = Visibility.Collapsed;
}
void PromptDialog_Loaded(object sender, RoutedEventArgs e)
{
if (_inputType == InputType.Password)
txtPasswordResponse.Focus();
else
txtResponse.Focus();
}
public static string Prompt(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
PromptDialog inst = new PromptDialog(question, title, defaultValue, inputType);
inst.ShowDialog();
if (inst.DialogResult == true)
return inst.ResponseText;
return null;
}
public string ResponseText
{
get
{
if (_inputType == InputType.Password)
return txtPasswordResponse.Password;
else
return txtResponse.Text;
}
}
private void btnOk_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
So you can call it like:
string repeatPassword = PromptDialog.Prompt("Repeat password", "Password confirm", inputType: PromptDialog.InputType.Password);
Great answer of Josh, all credit to him, I slightly modified it to this however:
MyDialog Xaml
<StackPanel Margin="5,5,5,5">
<TextBlock Name="TitleTextBox" Margin="0,0,0,10" />
<TextBox Name="InputTextBox" Padding="3,3,3,3" />
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Name="BtnOk" Content="OK" Grid.Column="0" Margin="0,0,5,0" Padding="8" Click="BtnOk_Click" />
<Button Name="BtnCancel" Content="Cancel" Grid.Column="1" Margin="5,0,0,0" Padding="8" Click="BtnCancel_Click" />
</Grid>
</StackPanel>
MyDialog Code Behind
public MyDialog()
{
InitializeComponent();
}
public MyDialog(string title,string input)
{
InitializeComponent();
TitleText = title;
InputText = input;
}
public string TitleText
{
get { return TitleTextBox.Text; }
set { TitleTextBox.Text = value; }
}
public string InputText
{
get { return InputTextBox.Text; }
set { InputTextBox.Text = value; }
}
public bool Canceled { get; set; }
private void BtnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = true;
Close();
}
private void BtnOk_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = false;
Close();
}
And call it somewhere else
var dialog = new MyDialog("test", "hello");
dialog.Show();
dialog.Closing += (sender,e) =>
{
var d = sender as MyDialog;
if(!d.Canceled)
MessageBox.Show(d.InputText);
}
You don't need ANY of these other fancy answers. Below is a simplistic example that doesn't have all the Margin, Height, Width properties set in the XAML, but should be enough to show how to get this done at a basic level.
XAML
Build a Window page like you would normally and add your fields to it, say a Label and TextBox control inside a StackPanel:
<StackPanel Orientation="Horizontal">
<Label Name="lblUser" Content="User Name:" />
<TextBox Name="txtUser" />
</StackPanel>
Then create a standard Button for Submission ("OK" or "Submit") and a "Cancel" button if you like:
<StackPanel Orientation="Horizontal">
<Button Name="btnSubmit" Click="btnSubmit_Click" Content="Submit" />
<Button Name="btnCancel" Click="btnCancel_Click" Content="Cancel" />
</StackPanel>
Code-Behind
You'll add the Click event handler functions in the code-behind, but when you go there, first, declare a public variable where you will store your textbox value:
public static string strUserName = String.Empty;
Then, for the event handler functions (right-click the Click function on the button XAML, select "Go To Definition", it will create it for you), you need a check to see if your box is empty. You store it in your variable if it is not, and close your window:
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
Calling It From Another Page
You're thinking, if I close my window with that this.Close() up there, my value is gone, right? NO!! I found this out from another site: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/
They had a similar example to this (I cleaned it up a bit) of how to open your Window from another and retrieve the values:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Cancel Button
You're thinking, well what about that Cancel button, though? So we just add another public variable back in our pop-up window code-behind:
public static bool cancelled = false;
And let's include our btnCancel_Click event handler, and make one change to btnSubmit_Click:
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
cancelled = true;
strUserName = String.Empty;
this.Close();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
cancelled = false; // <-- I add this in here, just in case
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
And then we just read that variable in our MainWindow btnOpenPopup_Click event:
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
// **Here we find out if we cancelled or not**
if (popup.cancelled == true)
return;
else
{
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Long response, but I wanted to show how easy this is using public static variables. No DialogResult, no returning values, nothing. Just open the window, store your values with the button events in the pop-up window, then retrieve them afterwards in the main window function.

What is the least intrusive way to display WPF tooltip on focus?

What is the minimum number of steps required to display a tooltip when the following control gets focus?
<TextBox ToolTip="Hello there!" ... />
I tried the following in GotFocus
private void ..._GotFocus(object sender, RoutedEventArgs e) {
var element = (FrameworkElement)sender;
var tooltip = element.ToolTip;
if (!(tooltip is ToolTip)) {
tooltip = new ToolTip { Content = tooltip };
element.ToolTip = tooltip;
}
((ToolTip)tooltip).IsOpen = true;
}
However, it seems to ignore the ToolTipService.Placement for this control and SystemParameters.ToolTipPopupAnimationKey set up level higher.
How can I make it work and honor all settings that generally work for tooltips (except the timing, obviously)?
I'd build an IsKeyboardFocused binding in the attached property, like this:
public class ShowOnFocusTooltip : DependencyObject
{
public object GetToolTip(...
public void SetToolTip(...
public static readonly DependencyProperty ToolTipProperty = DependencyProperty.RegisterAttached(..., new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
ToolTipService.SetToolTip(obj,
e.NewValue==null ? null :
BuildToolTip(obj, e.NewValue));
}
});
private object BuildToolTip(DependencyObject control, object content)
{
var tooltip = content is ToolTip ? (ToolTip)content : new ToolTip { Content = content };
tooltip.SetBinding(ToolTip.IsOpenProperty,
new Binding("IsKeyboardFocusWithin") { Source = control });
return tooltip;
}
Don't have a Windows machine to test, but I would have thought:
<TextBox x:Name="textBox">
<TextBox.ToolTip>
<ToolTip IsOpen="{Binding IsKeyboardFocusWithin, ElementName=textBox}">
Whatever
</ToolTip>
</TextBox.ToolTip>
</TextBox>

Resources