I have a window with version text boxes, buttons, and a ListBoxthat expands and contracts. Each ListBox item has multiple text boxes. I am trying to find a way to tab navigate from outside of the the ListBoxto the first TextBox in the first item for the ListBox. I can get it to navigate to the ListBox itself and if I hit the down arrow key it will select the first item but that is clumsy. I need it to tab directly from something outside the ListBox to something inside the ListBox.
Below is some of the XAML I use for the ListBox.
<ListBox x:Name="add_users_listbox" Margin="2,116,-8,0" BorderThickness="0" Height="322" Padding="0,0,0,0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Top"
HorizontalAlignment="Center" VerticalAlignment="Top"
SelectionMode="Single"
IsTabStop="True"
TabIndex="1004"
Background="{x:Null}" BorderBrush="{x:Null}"
ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding Add_User_Binding}"
SelectedIndex="{Binding Add_User_Selected_Index, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ListBox.Resources>
<Style TargetType="{x:Type ScrollBar}" BasedOn="{StaticResource ScrollBar_Rounded}"/>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource CustomListBoxItemStyle}"/>
<Style TargetType="{x:Type ListBox}" >
<Setter Property="KeyboardNavigation.TabNavigation" Value="Continue" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="60" Background="Transparent"
HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBox Height="26" Width="102" Padding="0,-1,0,1" Margin="2,2,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top" HorizontalContentAlignment="Left" VerticalContentAlignment="Center"
FontFamily="Segoe UI" FontSize="16" FontWeight="Bold"
TextWrapping="NoWrap" IsReadOnlyCaretVisible="True" UndoLimit="10" AllowDrop="False" MaxLines="1"
TabIndex="{Binding First_TabIndex}"
MaxLength="20"
TextChanged="first_last_textbox_TextChanged"
PreviewTextInput="first_last_textbox_PreviewTextInput"
Text="{Binding First_Textbox, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnDataErrors=True}">
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Simply make your ListBox IsTabStop="False" and the LitBoxItemStyle too.
I added this setter to the ListBoxItemStyle: <Setter Property="IsTabStop" Value="False"/>
<ListBox x:Name="add_users_listbox" Grid.Row="1" BorderThickness="0" Height="322" Padding="0,0,0,0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Top"
HorizontalAlignment="Center" VerticalAlignment="Top"
SelectionMode="Single"
IsTabStop="False"
TabIndex="1004"
Background="{x:Null}" BorderBrush="{x:Null}"
ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding Add_User_Binding}"
SelectedIndex="{Binding Add_User_Selected_Index, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ListBox.Resources>
<Style TargetType="{x:Type ScrollBar} BasedOn="{StaticResource ScrollBar_Rounded}" "/>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource CustomListBoxItemStyle}">
<Setter Property="IsTabStop" Value="False"/>
</Style>
<Style TargetType="{x:Type ListBox}" >
<Setter Property="KeyboardNavigation.TabNavigation" Value="Continue" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="60" Background="Transparent"
HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBox Height="26" Width="102" Padding="0,-1,0,1" Margin="2,2,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top" HorizontalContentAlignment="Left" VerticalContentAlignment="Center"
FontFamily="Segoe UI" FontSize="16" FontWeight="Bold"
TextWrapping="NoWrap" IsReadOnlyCaretVisible="True" UndoLimit="10" AllowDrop="False" MaxLines="1"
MaxLength="20"
TextChanged="first_last_textbox_TextChanged"
PreviewTextInput="first_last_textbox_PreviewTextInput"
Text="{Binding First_Textbox, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}">
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So, this is likely NOT the ideal way to do this, but it works. First we add a bound tag to the TextBox within the DataTemplate
ItemsSource="{Binding ListBox_Item_Collection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox Text="{Binding First_Textbox, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
Tag="{Binding Index}">
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
For simplicity I bound that tag to the corresponding index in the ItemSource collection.
int index = Add_User_Binding.Count;
ListBox_Item_Collection.Add(new SomeDataType()
{
Index = index,
The_Textbox = "Stuff in TextBox",
});
The next step is to add a KeyDown event in the user control that comes before this one. In that event we will find the element with that tag and then use the Dispatcher to focus it.
private void Preview_TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
string tag = "0";
IEnumerable<TextBox> elements = FindVisualChildren<TextBox>(this).Where(x => x.Tag != null && x.Tag.ToString() == tag);
foreach (TextBox element in elements)
{
FocusElement(element);
}
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private void FocusElement(IInputElement element)
{
if (element != null)
{
Dispatcher.BeginInvoke
(System.Windows.Threading.DispatcherPriority.ContextIdle,
new Action(delegate ()
{
Keyboard.Focus(element);
}));
}
}
As you can see, lots of work for something that should be much simpler.
I am working in Visual Studio 2013 in WPF (C#) and I have the following code to create an expander from my xml. It is currently working perfectly right now but I want to include an expand all and collapse all buttons. I have looked all over and can't seem to find a solution.
Here is where the expander is created. I know I just have to iterate through a list of items and change the Property="IsExpanded" to Value="True" to create an expand all.
<DataTemplate x:Key="dtListTemplate" >
<StackPanel>
<Expander LostFocus="CollapseExpander" ExpandDirection="Down" Width="Auto">
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="IsExpanded" Value="False" />
<Setter Property="Header" Value="{Binding XPath=#Name}" />
<Setter Property="FontWeight" Value="Bold"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded,RelativeSource={RelativeSource Self}}" Value="True">
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<ListBox Name="itemsList"
ItemsSource="{Binding XPath=UpgradeAction}"
ItemTemplate="{StaticResource dtListItemTemplate}"
SelectionChanged="listItems_SelectionChanged"
Style="{StaticResource styleListBoxUpgradeAction}"
ItemContainerStyle="{StaticResource styleListBoxItemUpgradeAction}">
</ListBox>
</Expander>
</StackPanel>
</DataTemplate>
Here's the code that calls the DataTemplate which has information from an Xml.
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Border Grid.Column="0" Grid.Row="0" Width="790" Height="40" Padding="5" Background="#4E87D4">
<Label VerticalAlignment="Center" FontSize="16" FontWeight="Bold" Foreground="White">Test</Label>
</Border>
<Button Name="ExpandBttn" Width="100" Height="40" FontSize="16" FontWeight="Bold" Content="Expand All" DataContext="{Binding}" Click="Expand_Button_Click"/>
<Button Name="ColapseBttn" Width="100" Height="40" FontSize="16" FontWeight="Bold" Content="Colapse All" DataContext="{Binding}" Click="Collapse_Button_Click"/>
</StackPanel>
<ListView Name="listItems" Grid.Column="0" Grid.Row="1" Background="Wheat"
ItemsSource="{Binding Source={StaticResource xmldpUpgradeActions}, XPath=ActionGroup}"
ItemTemplate="{StaticResource dtListTemplateRichards}"
SelectionChanged="listItems_SelectionChanged">
</ListView>
</StackPanel>
Here's what I tried in the .cs file for the expand all portion.
private void Expand_Button_Click(object sender, RoutedEventArgs e)
{
foreach(var item in listItems.Items)
{
var listBoxItem = listItems.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
var itemExpander = (Expander)GetExpander(listBoxItem);
if (itemExpander != null)
itemExpander.IsExpanded = true;
}
}
private static DependencyObject GetExpander(DependencyObject container)
{
if (container is Expander) return container;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(container); i++)
{
var child = VisualTreeHelper.GetChild(container, i);
var result = GetExpander(child);
if (result != null)
{
return result;
}
}
return null;
}
Any help is greatly appreciated!
Is xmldpUpgradeActions a CollectionViewSource?
Whatever class is in the collection, it should implement INotifyPropertyChanged.
Give it an IsExpanded property that raises PropertyChanged in its setter when its value changes, and bind that to Expander.IsExpanded in the template:
<Expander
IsExpanded="{Binding IsExpanded}"
LostFocus="CollapseExpander"
ExpandDirection="Down"
Width="Auto">
Write a command that loops through all the items in the collection and sets item.IsExpanded = false; on each one.
The picture shows an example of some items added in my datagrid. Right now I can only change each rows checkbox by clicking on it but I would like to be able to click anywhere on each row to change that rows checkbox.
XAML of datagrid and row template:
<DataGrid Grid.Row="1" SnapsToDevicePixels="True" BorderThickness="0" SelectedItem="{Binding Selected}" ItemsSource="{Binding Source={StaticResource ColViewSource},Mode=OneWay}" AutoGenerateColumns="False" x:Name="dataGrid" HeadersVisibility="None" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter></DataGridRowsPresenter>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="TTC">
<Setter Property="Template" Value="{StaticResource ResourceKey=TTC}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="TTC">
<Grid Style="{StaticResource rowGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="30"/>
<ColumnDefinition />
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Style="{StaticResource commentboxStyle}" Text="{Binding Comment,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="0" Grid.ColumnSpan="3" ></TextBox>
<TextBlock Style="{StaticResource textblockStyle}" Text="{Binding Text1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Style="{StaticResource labelStyle}" Content="{Binding Text2,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="2" HorizontalAlignment="Right" />
<CheckBox Style="{StaticResource RectBox}" IsChecked="{Binding IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="3" HorizontalAlignment="Center" IsThreeState="True"/>
<Path Style="{StaticResource dottedpathStyle}"/>
</Grid>
</ControlTemplate>
Any tips on how to achieve this?
You could add a handler to your DataGridRow style that sets the IsChecked property of the data object to true:
<Style TargetType="DataGridRow">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnPreviewMouseLeftButtonDown" />
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="TTC">
<Setter Property="Template" Value="{StaticResource ResourceKey=TTC}"/>
</DataTrigger>
</Style.Triggers>
</Style>
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!(e.Source is CheckBox))
{
DataGridRow row = sender as DataGridRow;
YourDataClass dataObject = row.DataContext as YourDataClass;
if (dataObject != null)
dataObject.IsChecked = true;
}
}
Make sure that YourDataClass implements the INotifyPropertyChanged interface and raises the PropertyChanged event in the setter of the IsChecked property.
Prevent Code behind and use a System.Windows.Interactivity.Behaviour.
public class SelectedCheckBoxBehaviour : System.Windows.Interactivity.Behavior<FrameworkElement>
{
protected override void OnAttached()
{
AssociatedObject.MouseDown += AssociatedObject_MouseDown;
base.OnAttached();
}
private void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
FrameworkElement self = sender as FrameworkElement;
if(self != null)
{
MyViewModel dataContext = self.DataContext as MyViewModel;
dataContext.IsChecked = !dataContext.IsChecked;
}
}
protected override void OnDetaching()
{
AssociatedObject.MouseDown -= AssociatedObject_MouseDown;
base.OnDetaching();
}
}
Namespaces
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviour="clr-namespace:WpfApplication.Behaviour"
<!-- ... --->
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="30"/>
<ColumnDefinition />
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Comment,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="0" Grid.ColumnSpan="3" ></TextBox>
<TextBlock Text="{Binding Text1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="{Binding Text2,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="2" HorizontalAlignment="Right" />
<CheckBox IsChecked="{Binding IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="3" HorizontalAlignment="Center" IsThreeState="True"/>
<Path />
<i:Interaction.Behaviors>
<behaviour:SelectedCheckBoxBehaviour />
</i:Interaction.Behaviors>
</Grid>
This should work - Tut
I would like to create an animation within my grid. I have a 5x5 Grid, each grid shows a button. After the grid is loaded one of the buttons should randomliy change his color to green. After 1 seconds this button should change back and another should change his color to green.
If the user is able to reach the this button within this 1 second with his mouse (mouseover) the button should change his color to red and stay red. The next button who changes his color to green should not be this one.
This should be a little game. My question is, what is the easiest way to implement this game.
Please help me!
<Page x:Class="LeapTest.Layout"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:LeapTest"
mc:Ignorable="d"
d:DesignHeight="1050" d:DesignWidth="1000"
Title="Layout">
<Page.Resources>
<Style x:Key="pageTitle" TargetType="TextBlock">
<Setter Property="Background" Value="DimGray"/>
<Setter Property="FontSize" Value="40"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="Padding" Value="0,5,0,5"/>
</Style>
<Style x:Key="Grid" TargetType="Grid">
<Setter Property="Background" Value="White"/>
</Style>
<Style x:Key="Button" TargetType="Button">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="Green"/>
<Setter Property="BorderThickness" Value="2"/>
</Style>
</Page.Resources>
<Grid Style="{StaticResource Grid}">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="200" />
<RowDefinition Height="200" />
<RowDefinition Height="200" />
<RowDefinition Height="200" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.ColumnSpan="5" Style="{StaticResource pageTitle}"> LEAP Motion </TextBlock>
<Button Name ="BTN_0_0" Grid.Column="0" Grid.Row="1" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_0_1" Grid.Column="1" Grid.Row="1" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_0_2" Grid.Column="2" Grid.Row="1" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_0_3" Grid.Column="3" Grid.Row="1" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_0_4" Grid.Column="4" Grid.Row="1" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_1_0" Grid.Column="0" Grid.Row="2" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_1_1" Grid.Column="1" Grid.Row="2" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_1_2" Grid.Column="2" Grid.Row="2" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_1_3" Grid.Column="3" Grid.Row="2" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_1_4" Grid.Column="4" Grid.Row="2" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_2_0" Grid.Column="0" Grid.Row="3" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_2_1" Grid.Column="1" Grid.Row="3" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_2_2" Grid.Column="2" Grid.Row="3" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_2_3" Grid.Column="3" Grid.Row="3" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_2_4" Grid.Column="4" Grid.Row="3" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_3_0" Grid.Column="0" Grid.Row="4" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_3_1" Grid.Column="1" Grid.Row="4" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_3_2" Grid.Column="2" Grid.Row="4" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_3_3" Grid.Column="3" Grid.Row="4" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_3_4" Grid.Column="4" Grid.Row="4" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_4_0" Grid.Column="0" Grid.Row="5" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_4_1" Grid.Column="1" Grid.Row="5" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_4_2" Grid.Column="2" Grid.Row="5" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_4_3" Grid.Column="3" Grid.Row="5" Click="BTN_Click" Style="{StaticResource Button}"/>
<Button Name ="BTN_4_4" Grid.Column="4" Grid.Row="5" Click="BTN_Click" Style="{StaticResource Button}"/>
</Grid>
//BackEnd Code
private void BTN_Click(object sender, RoutedEventArgs e)
{
//BTN1.Background = Brushes.Green;
Button btnTest = (Button)sender;
if (btnTest.Background == Brushes.Green)
{
btnTest.Background = Brushes.White;
}
else
{
btnTest.Background = Brushes.Green;
}
}
you should loop over all the button controls on the form with a special name or tag. Save this in an array and select one at random and change it's color. if the mouseover finds the one with the right color recall the method to find a control at random and so on.
This should be pretty straight forward. And since this looks like homework. I won't solve it for you as you wouldn't learn anything. Think about what needs to be done and google each part seperately. try to understand it and then continue! goodluck!
EDIT : regarding your question in a strict way, you do not need animation to acheive what you want to do, unless your need is to animate color changes.
Here is a full working example not using MVVM but using a collection of models representing the buttons.
The main window/page code handling all the logic (Random, model changes, etc.):
public partial class MainWindow : Window, INotifyPropertyChanged
{
private const int BTN_NUMBERS = 25;
private ObservableCollection<ButtonModel> _buttonsCollection;
private ButtonModel _currentTarget;
public ObservableCollection<int> ExcludedItems
{
get { return _excludedItems; }
private set { _excludedItems = value; OnPropertyChanged(); }
}
private Random _rnd;
private Timer _timer;
private ObservableCollection<int> _excludedItems = new ObservableCollection<int>();
public MainWindow()
{
DataContext = this;
InitializeComponent();
ButtonsCollection = new ObservableCollection<ButtonModel>();
for (int i = 0; i < BTN_NUMBERS; i++)
{
ButtonsCollection.Add(new ButtonModel() { ButtonNumber = i });
}
Start();
}
private void Start()
{
_currentTarget = null;
foreach (var bm in ButtonsCollection)
{
bm.IsCurrentTarget = bm.IsReached = false;
}
ExcludedItems.Clear();
_rnd = new Random(DateTime.Now.Second);
_timer = new Timer(OnTargetChanged, null, 0, 1000);
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ButtonModel> ButtonsCollection
{
get { return _buttonsCollection; }
set { _buttonsCollection = value; OnPropertyChanged(); }
}
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void Btn_candidate_OnMouseEnter(object sender, MouseEventArgs e)
{
ButtonModel model = ((Button)sender).DataContext as ButtonModel;
if (model != null && model.IsCurrentTarget && !ExcludedItems.Contains(model.ButtonNumber))
{
model.IsReached = true;
ExcludedItems.Add(model.ButtonNumber);
}
}
private void ChangeTarget()
{
var target = GetNextTarget();
if (target == -1)
{
if (_timer != null)
{
_timer.Dispose();
_timer = null;
}
MessageBox.Show("All items have been reached ! Congratulations");
}
if (_currentTarget != null) _currentTarget.IsCurrentTarget = false;
_currentTarget = ButtonsCollection[target];
_currentTarget.IsCurrentTarget = true;
}
private int GetNextTarget()
{
if (ExcludedItems.Count == BTN_NUMBERS)
{
return -1;
}
var target = _rnd.Next(0, BTN_NUMBERS);
if (ExcludedItems.Contains(target))
{
return GetNextTarget();
}
return target;
}
private void OnTargetChanged(object state)
{
this.Dispatcher.InvokeAsync(ChangeTarget);
}
private void Btn_startover_OnClick(object sender, RoutedEventArgs e)
{
Start();
}
}
The model representing the buttons, that contains the code that triggers XAML changes :
public class ButtonModel : INotifyPropertyChanged
{
private bool _isCurrentTarget;
private bool _isReached;
public event PropertyChangedEventHandler PropertyChanged;
public int ButtonNumber { get; set; }
public bool IsCurrentTarget
{
get { return _isCurrentTarget; }
set { _isCurrentTarget = value; OnPropertyChanged(); }
}
public bool IsReached
{
get { return _isReached; }
set { _isReached = value; OnPropertyChanged(); }
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And finally, and most importantly, the simplified XAML code :
<Window x:Class="GridAnimame.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:GridAnimame"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance {x:Type local:MainWindow}}"
Title="MainWindow" Height="500" Width="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="400"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding ButtonsCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Name="btn_candidate" MouseEnter="Btn_candidate_OnMouseEnter" Margin="1">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="brd_btn_layout" Background="LightGray" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding ButtonNumber}" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Black"></TextBlock>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsCurrentTarget}" Value="true">
<Setter TargetName="brd_btn_layout" Property="Background" Value="Green"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsReached}" Value="true">
<Setter TargetName="brd_btn_layout" Property="Background" Value="Red"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding ExcludedItems}"></ListBox>
<Button x:Name="btn_startover" Grid.Row="1" Grid.Column="1" Content="Start Over !" Margin="2" Click="Btn_startover_OnClick"></Button>
</Grid>
Althought the logic could be improved, here are the key points of this sample :
You don't declare all the buttons on a static way in the XAML. Instead, your main model holds a collection of models representing the buttons and containing all the data that will trigger the XAML (visual) changes. Thus, the control in XAML representing the grid is bound to this collection
The logic is made throught code and the visual effect of this loggic is reflected using triggers declared in XAML
Only 2 things still have to be done to have a clean approach : 1)The main window logic can now be easily be encapsulated into a real view model which will serve as the window data context and 2) the events should be replaced by DelegateCommands
I'm create an module which is a Editing Bill module which will enable the user to edit the available in the database. I'm facing one validation problem which is that when the user don't want to edit the bill & click on the "Cancel" button will back to the listview to continue select another record. Here is my problem come as when the user didn't make any modification & accidentally click on the "Status" combo from "UNPAID" to "PAID" then directly quite. When re select the record then the record status value will become "PAID" instead of "UNPAID" which is the actual value.
How can I do so that when every time the user don't make any modification & return back to the listview will set back the default value for the combobox.
Thank you all for viewing this question & hope to receive reply :)
I will attach my code so that easily to refer:
XAML File
<Window x:Class="HouseWivesSavior.HomecareModule.EditBillRemainder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="EditBillRemainder" Height="600" Width="800" Loaded="Window_Loaded">
<Window.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFFFFFFF" Offset="0"/>
<GradientStop Color="#FFFF00FF" Offset="1"/>
</LinearGradientBrush>
</Window.Background>
<Window.Resources>
<DataTemplate x:Key="MyDataTemplate">
<Border BorderBrush="#FF000000" BorderThickness="1,1,0,1" Margin="-6,-2,-6,-2">
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding Path=BillName}"/>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate x:Key="MyDataTemplate2">
<Border BorderBrush="#FF000000" BorderThickness="1,1,0,1" Margin="-6,-2,-6,-2">
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding Path=BillDescription}"/>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate x:Key="MyDataTemplate3">
<Border BorderBrush="#FF000000" BorderThickness="1,1,0,1" Margin="-6,-2,-6,-2">
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding Path=BillAmount}"/>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate x:Key="MyDataTemplate4">
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="-6,-2,-6,-2">
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding Path=BillDueDate, StringFormat='dd-MM-yyyy'}"/>
</StackPanel>
</Border>
</DataTemplate>
<Style x:Key="MyItemContainerStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<!--<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" />-->
</Style>
<!--DatePickerTextBoxFormat-->
<Style TargetType="{x:Type DatePickerTextBox}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<TextBox x:Name="PART_TextBox" Text="{Binding Path=SelectedDate, StringFormat='dd-MM-yyyy', RelativeSource={RelativeSource AncestorType={x:Type DatePicker}}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListView Name="lvwBooks" Background="White" ItemsSource="{Binding}" Margin="131,130,4,6" ItemContainerStyle="{DynamicResource MyItemContainerStyle}">
<ListView.View>
<GridView>
<GridViewColumn Header="Bill Name" Width="100" CellTemplate="{DynamicResource MyDataTemplate}"/>
<GridViewColumn Header="Description" Width="250" CellTemplate="{DynamicResource MyDataTemplate2}"/>
<GridViewColumn Header="Amount" Width="160" CellTemplate="{DynamicResource MyDataTemplate3}"/>
<GridViewColumn Header="Due Date" Width="100" CellTemplate="{DynamicResource MyDataTemplate4}"/>
<GridViewColumn Header="Edit">
<GridViewColumn.CellTemplate>
<DataTemplate>
<UserControl>
<Hyperlink Click="InputBox_Click">Edit
<Hyperlink.Style>
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="Hyperlink.IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Path=IsSelected}" Value="True">
<Setter Property="Hyperlink.IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Hyperlink.Style>
</Hyperlink>
</UserControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<!-- It's important that this is in the end of the XAML as it needs to be on top of everything else! -->
<Grid x:Name="InputBox" Visibility="Collapsed">
<Grid Background="Black" Opacity="0.5" />
<Border
MinWidth="250"
Background="Orange"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="0,40,0,40"
HorizontalAlignment="Center"
VerticalAlignment="Center" Height="493" Margin="130,30,120,38" Width="528">
<Canvas Height="446" Width="524">
<GroupBox Header="General Information" Height="141" HorizontalAlignment="Left" Margin="193,52,0,0" Name="groupBox1" VerticalAlignment="Top" Width="495" Canvas.Left="-173" Canvas.Top="-67">
<Canvas Background="WhiteSmoke" Opacity="1">
<GridSplitter Background="#FFBCBCBC" HorizontalAlignment="Left" Margin="207,13,0,140" ResizeBehavior="PreviousAndNext" Width="2" Height="50" Canvas.Left="-3" Canvas.Top="48" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="6,6,0,0" Name="textBlock1" Text="Bill Name" VerticalAlignment="Top" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="215,65,0,0" Name="textBlock4" Text="Due Date" VerticalAlignment="Top" Grid.ColumnSpan="2" />
<GridSplitter HorizontalAlignment="Left" ResizeBehavior="PreviousAndNext"
Width="1" Background="#FFBCBCBC" Margin="206,63,0,9" />
<DatePicker Height="25" HorizontalAlignment="Right" Margin="0,80,157,0" Name="BillDueDatedatePicker" VerticalAlignment="Top" Width="115" Grid.ColumnSpan="2" Canvas.Left="215" Canvas.Top="6" Text="{Binding Path=BillDueDate, StringFormat='dd-MM-yyyy'}" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}" SelectedDateFormat="Short"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="7,29,0,0" Name="BillNameTxtBox" VerticalAlignment="Top" Width="458" Grid.ColumnSpan="2" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}" Text="{Binding Path=BillName}"/>
<TextBlock Height="23" HorizontalAlignment="Left" Margin="7,63,0,0" Name="textBlock8" Text="Bill Type" VerticalAlignment="Top" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="8,81,0,0" Name="BillTypeCboBox" VerticalAlignment="Top" Width="193" Canvas.Left="-5" Canvas.Top="8" Text="{Binding Path=BillTypes}" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}">
<ComboBoxItem Content="Water" />
<ComboBoxItem Content="Electricity" />
<ComboBoxItem Content="Gas" />
<ComboBoxItem Content="Internet/broadband"/>
<ComboBoxItem Content="Others"/>
</ComboBox>
<TextBox Canvas.Left="62" Canvas.Top="3" Height="23" Name="textBox1" Width="120" Visibility="Visible" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}" Text="{Binding Path=BillNo}"/>
</Canvas>
</GroupBox>
<GroupBox Header="Payment Information" Height="339" HorizontalAlignment="Left" Margin="193,202,0,0" Name="groupBox2" VerticalAlignment="Top" Width="495" Canvas.Left="-174" Canvas.Top="-81">
<Canvas Background="WhiteSmoke" Height="303" Visibility="Visible">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="7,17,0,0" Name="textBlock6" Text="Amount Due" VerticalAlignment="Top" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="7,75,0,0" Name="textBlock7" Text="Description" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="81,14,0,0" Name="BillAmountTxtBox" VerticalAlignment="Top" Width="120" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}" Text="{Binding Path=BillAmount}"/>
<TextBox Height="148" HorizontalAlignment="Left" Margin="4,96,0,0" Name="BillDescriptionTxtBox" VerticalAlignment="Top" Width="471" VerticalScrollBarVisibility="Visible" SpellCheck.IsEnabled="True" TextWrapping="Wrap" AcceptsReturn="True" Text="{Binding Path=BillDescription}" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}"/>
<TextBlock Height="23" HorizontalAlignment="Left" Margin="6,46,0,0" Name="textBlock9" Text="Status" VerticalAlignment="Top" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="81,46,0,0" Name="BillStatusCboBox" VerticalAlignment="Top" Width="120" SelectionChanged="comboBox3_SelectionChanged" Text="{Binding Path=BillStatus}" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}" >
<ComboBoxItem Content="UNPAID" />
<ComboBoxItem Content="PAID" />
</ComboBox>
<TextBlock Height="23" HorizontalAlignment="Left" Margin="215,14,0,0" Name="textBlock10" Text="Bill is Paid:" VerticalAlignment="Top" />
<DatePicker Text="{Binding Path=BillPaidDate}" Height="25" HorizontalAlignment="Left" Margin="212,37,0,0" Name="BillPaidDatedatePicker" VerticalAlignment="Top" Width="115" IsEnabled="false" DataContext="{Binding ElementName=lvwBooks, Path=SelectedItem}"/>
<GridSplitter Background="#FFBCBCBC" HorizontalAlignment="Left" Margin="205,14,0,228" ResizeBehavior="PreviousAndNext" Width="2" />
<Separator Height="26" HorizontalAlignment="Left" Margin="7,250,0,0" Name="separator1" VerticalAlignment="Top" Width="470" />
<Button Content="Done" Height="23" HorizontalAlignment="Left" Margin="322,267,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="YesButton_Click" />
<Button Content="Cancel" Height="23" HorizontalAlignment="Left" Margin="402,266,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="NoButton_Click" />
<GridSplitter Background="#FFBCBCBC" HorizontalAlignment="Left" Margin="207,13,0,140" ResizeBehavior="PreviousAndNext" Width="0" />
</Canvas>
</GroupBox>
</Canvas>
</Border>
</Grid>
</Grid>
</Window>
Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Data.SqlClient;
using System.Configuration;
using System.Data;
namespace HouseWivesSavior.HomecareModule
{
/// <summary>
/// Interaction logic for EditBillRemainder.xaml
/// </summary>
public partial class EditBillRemainder : Window
{
public EditBillRemainder()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ShowData();
}
private void InputBox_Click(object sender, RoutedEventArgs e)
{
// CoolButton Clicked! Let's show our InputBox.
InputBox.Visibility = System.Windows.Visibility.Visible;
}
private void NoButton_Click(object sender, RoutedEventArgs e)
{
//ShowData();
// NoButton Clicked! Let's hide our InputBox.
InputBox.Visibility = System.Windows.Visibility.Collapsed;
}
private void ListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var item = sender as ListViewItem;
if (item != null && item.IsSelected)
{
}
}
//Design
private void comboBox3_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
////The logic for this will be stanger but in order to cope iwht the SelectionChange behavior.
//string s1 = BillStatusCboBox.SelectionBoxItem.ToString();
//// MessageBox.Show(s1);
//if (s1.Equals("UNPAID"))
//{
// BillPaidDatedatePicker.IsEnabled = true;
// BillPaidDatedatePicker.Text = DateTime.Now.ToString("d/MM/yyyy");
//}
//if (s1.Equals("PAID"))
//{
// BillPaidDatedatePicker.IsEnabled = false;
// //BillStatusCboBox.SelectedIndex = 0;
//}
ComboBoxItem currentItem = ((System.Windows.Controls.ComboBoxItem)BillStatusCboBox.SelectedItem);
if (currentItem.Content.Equals("PAID"))
{
BillPaidDatedatePicker.IsEnabled = true;
// BuilderupdateButton.Visibility = Visibility.Visible;
}
else {
BillPaidDatedatePicker.IsEnabled = false;
}
}
//Database
public void ShowData()
{
SqlConnection conn;
string connStr = ConfigurationManager.ConnectionStrings["house"].ConnectionString;
conn = new SqlConnection(connStr);
//SqlConnection con = new SqlConnection(#"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database1.mdf;Integrated Security=True;User Instance=True");
conn.Open();
SqlCommand comm = new SqlCommand("Select * from bill ORDER BY BillDueDate", conn);
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter(comm);
da.Fill(dt);
lvwBooks.DataContext = dt.DefaultView;
}
private void YesButton_Click(object sender, RoutedEventArgs e)
{
Update();
// YesButton Clicked! Let's hide our InputBox and handle the input text.
InputBox.Visibility = System.Windows.Visibility.Collapsed;
}
public void Update() {
SqlConnection conn;
SqlCommand cmdInsert;
String strUpdate;
string connStr = ConfigurationManager.ConnectionStrings["house"].ConnectionString;
conn = new SqlConnection(connStr);
strUpdate = "UPDATE bill SET BillName=#BillName, BillTypes=#BillTypes, BillDueDate=#BillDueDate, BillAmount=#BillAmount, BillStatus=#BillStatus, BillPaidDate=#BillPaidDate, BillDescription=#BillDescription WHERE BillNo='" + textBox1.Text + "' ";
cmdInsert = new SqlCommand(strUpdate, conn);
cmdInsert.Parameters.AddWithValue("#BillName", BillNameTxtBox.Text);
cmdInsert.Parameters.AddWithValue("#BillTypes", BillTypeCboBox.Text);
cmdInsert.Parameters.AddWithValue("#BillDueDate", DateTime.Parse(BillDueDatedatePicker.Text));
cmdInsert.Parameters.AddWithValue("#BillAmount", BillAmountTxtBox.Text);
cmdInsert.Parameters.AddWithValue("#BillStatus", BillStatusCboBox.Text);
//MessageBox.Show(BillPaidDatedatePicker.Text);
if (BillPaidDatedatePicker.Text.Equals(""))
{
cmdInsert.Parameters.AddWithValue("#BillPaidDate", "");
}
else {
cmdInsert.Parameters.AddWithValue("#BillPaidDate", DateTime.Parse(BillPaidDatedatePicker.Text));
}
// cmdInsert.Parameters.AddWithValue("#BillPaidDate", DateTime.Parse(empty));
cmdInsert.Parameters.AddWithValue("#BillDescription", BillDescriptionTxtBox.Text);
try
{
conn.Open();
int result = cmdInsert.ExecuteNonQuery();
if (result == 1)
{
MessageBox.Show("Bill Edited!!!");
}
}
catch (SqlException ex)
{
MessageBox.Show(ex.ToString());
}
finally {
conn.Close();
}
}
}
}
For this case it would be better to user IDataErrorInfo interface instead of using ValiationRule since IDataErrorInfo will be handled inside ViewModel where you also have all the necessary database statements.
Here is a link to a tutorial explaining how to do validation with IDataErrorInfo.
http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/
Have fun :)
I solved it by like this:
Code-Behind:
The problem that I faced is NullReferenceException then I used a try..catch to catch the "NullReferenceException"
private void comboBox3_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem currentItem = ((System.Windows.Controls.ComboBoxItem)BillStatusCboBox.SelectedItem);
try
{
if (currentItem.Content.Equals("PAID"))
{
BillPaidDatedatePicker.IsEnabled = true;
// BuilderupdateButton.Visibility = Visibility.Visible;
}
else
{
BillPaidDatedatePicker.IsEnabled = false;
}
}
catch (NullReferenceException ex){ // At here which you can see.
//I purposely set it to do nothing when catch it.
}
Then when the user accidentally click on the "UPAID" to "PAID" & directly click on the cancel button will invoke "NoButton_Click" which will try to reload & select the value from the database:
private void NoButton_Click(object sender, RoutedEventArgs e)
{
ShowData(); // Invoke & select from database again.
// NoButton Clicked! Let's hide our InputBox.
InputBox.Visibility = System.Windows.Visibility.Collapsed;
}
I know by this way solving my problem will be quite inefficient but get the job done :) Which is fine to me.