I'm developing a WPF application utilizing the MVVM design pattern, and I'd like to display a button matrix with the help of a UniformGrid with each gridcell being a colorable field.
I have a Grid having 4 rows set up inside my MainWindow.xaml.
The first row inside it holds a menu, the second contains a button.
A StackPanel is contained by the third Grid row with three buttons as children to the stackpanel. Their purpose is to mark which UniformGrid size will be in use.
I'd like to place the UniformGrid inside the fourth row in such a way that the third row of the main grid shall collapse just prior to the emergence of the buttonmatrix in order to ensure enough space required by the UiformGrid. However, I do not want the matrix to fill the whole window.
I'd like to modify the color of an individual matrixcell using an ObservableCollection with each element of the collection being a SolidColorBrush instance.
The problem is that if I click on one of the buttons of the StackPanel then the panel itself vanishes (as desired), however the buttonmatrix cannot be seen.
Any help is greatly appreciated!
View (MainWindow.xaml)
<Window x:Class="Game.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="GAME" Height="600" Width="800">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="visibilityConverter" />
</Window.Resources>
<Grid x:Name="mainGrid" Margin="0,0,0,171">
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition Height="22" />
<RowDefinition Height="{Binding GridThirdRowHeight}" />
<RowDefinition Height="500" />
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Header="File">
<MenuItem Header="New game" Command="{Binding NewGameCommand}" />
<Separator />
<MenuItem Header="Load game" Command="{Binding LoadGameCommand}" />
<MenuItem Header="Save game" Command="{Binding SaveGameCommand}" />
<Separator />
<MenuItem Header="Exit" Command="{Binding ExitCommand}" />
</MenuItem>
</Menu>
<Button x:Name="buttonStartStopGame" Grid.Row="1" Background="Red" Foreground="White" Content="{Binding ButtonStartStopGameContent}" Height="22" Width ="66" HorizontalAlignment="Right" Command="{Binding ButtonStartStopGameClickCommand}"/>
<StackPanel x:Name="panel1" Grid.Row="2" Visibility = "{Binding IsPanelVisible, Converter={StaticResource visibilityConverter}}" Orientation="Vertical" Margin="124,112,191,110" Background="#66808080">
<Button x:Name="buttonSmallTable" Background="Red" Foreground="White" Content="15 X 15" Height="22" Width="66" HorizontalAlignment="Center" Margin="0,0,0,0" Command="{Binding ButtonSmallTableClickCommand}"/>
<Button x:Name="buttonMediumTable" Background="Red" Foreground="White" Content="17 X 17" Height="22" Width="66" HorizontalAlignment="Center" Margin="0,25,0,0" Command="{Binding ButtonMediumTableClickCommand}"/>
<Button x:Name="buttonLargeTable" Background="Red" Foreground="White" Content="19 X 19" Height="22" Width="66" HorizontalAlignment="Center" Margin="0,25,0,0" Command="{Binding ButtonLargeTableClickCommand}"/>
</StackPanel>
<ItemsControl Grid.Row="3" ItemsSource="{Binding Fields}">
<!--controller containing buttons-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!--we insert the UniformGrid here-->
<UniformGrid Rows="{Binding GameTableRowCount}" Columns="{Binding GameTableColCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<!--buttons being utilized as UniformGrid elements-->
<DataTemplate>
<Button Background="Green" Focusable="False" RenderTransformOrigin="0.5, 0.5">
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
ViewModel
private GameModel _model; // modell
private Boolean _isGameOn = false;
private Boolean _isPanelVisible = true;
private String _gridThirdRowHeight = "Auto";
private Int32 _gameTableRowCount;
private Int32 _gameTableColCount;
private String _buttonStartStopGameContent = "START";
#endregion
#region Properties
public String GridThirdRowHeight
{
get { return _gridThirdRowHeight; }
set { _gridThirdRowHeight = value;
OnPropertyChanged(nameof(GridThirdRowHeight)); }
}
public Boolean IsGameOn
{
get { return _isGameOn; }
set { _isGameOn = value; }
}
/// <summary>
/// Query and modification for the number of rows inside the gametable.
/// </summary>
public Int32 GameTableRowCount
{
get { return _gameTableRowCount; }
set
{
_gameTableRowCount = value;
OnPropertyChanged(nameof(GameTableRowCount));
}
}
/// <summary>
/// Query and modification for the number of columns inside the gametable.
/// </summary>
public Int32 GameTableColCount
{
get { return _gameTableColCount; }
set
{
_gameTableColCount = value;
OnPropertyChanged(nameof(GameTableColCount));
}
}
public Boolean IsPanelVisible
{
get { return _isPanelVisible; }
set
{
_isPanelVisible = value;
OnPropertyChanged(nameof(IsPanelVisible));
}
}
...
public DelegateCommand NewGameCommand { get; private set; }
public DelegateCommand SaveGameCommand { get; private set; }
public DelegateCommand LoadGameCommand { get; private set; }
public DelegateCommand ExitCommand { get; private set; }
public DelegateCommand ButtonSmallTableClickCommand { get; }
public DelegateCommand ButtonMediumTableClickCommand { get; }
public DelegateCommand ButtonLargeTableClickCommand { get; }
public DelegateCommand ButtonStartStopGameClickCommand { get; }
public ObservableCollection<SolidColorBrush> Fields { get; set; }
...
private void GenerateGameTable()
{
Fields = new ObservableCollection<SolidColorBrush>();
GridThirdRowHeight = "0";
GameTableRowCount = _model.GameTable.GetLength(0);
GameTableColCount = _model.GameTable.GetLength(1);
for (Int32 i = 0; i < _model.GameTable.GetLength(0); i++)
{
for (Int32 j = 0; j < _model.GameTable.GetLength(1); j++)
{
Fields.Add(new SolidColorBrush());
Fields[i * _model.GameTable.GetLength(1) + j] = Brushes.Green;
}
}
}
...
private async void ViewModel_ButtonSmallTableClick(object? sender, System.EventArgs e)
{
IsPanelVisible = false;
//here I generate the contents of the gametable
//for the model layer
GenerateGameTable(); // purpose: to create the visible matrix
}
}
Related
I have a ListView. Inside of this ListView there is ItemsControl and inside it there is second ItemsControl. Inside of the second ItemsControl there are TextBoxes.
ListView -> ItemsControl -> ItemsControl -> TextBox
Is there any chance that I would be able to get index of ListViewItem, which specific TextBox belongs to after clicking on this TextBox?
For example
I select a ListViewItem on index 0 but then I click on TextBox which belong to ListViewItem on index 2. In that case I would like to change value of SelectedGroupIndex from 0 to 2.
"Hello" strings are just for testing.
Thank you very much.
ViewModel
public class MainWindowViewModel
{
public ObservableCollection<ObservableCollection<ObservableCollection<ListViewString>>> AllTexts { get; set; }
public int SelectedGroupIndex { get; set; }
public ICommand AddGroup { get; private set; }
public ICommand AddColumn { get; private set; }
public ICommand TextBoxSelected { get; private set; }
public MainWindowViewModel()
{
this.AllTexts = new ObservableCollection<ObservableCollection<ObservableCollection<ListViewString>>>();
this.SelectedGroupIndex = -1;
this.AddGroup = new Command(this.AddGroupCommandHandler);
this.AddColumn = new Command(this.AddColumnCommandHandler);
this.TextBoxSelected = new Command(this.TextBoxSelectedCommandHandler);
}
private void AddGroupCommandHandler()
{
var tempColumn = new ObservableCollection<ListViewString>() {
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello") };
var tempGroup = new ObservableCollection<ObservableCollection<ListViewString>>();
tempGroup.Add(tempColumn);
this.AllTexts.Add(new ObservableCollection<ObservableCollection<ListViewString>>(tempGroup));
}
private void AddColumnCommandHandler()
{
if (this.SelectedGroupIndex >= 0 && this.SelectedGroupIndex < this.AllTexts.Count)
{
var tempColumn = new ObservableCollection<ListViewString>() {
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello") };
this.AllTexts[this.SelectedGroupIndex].Add(tempColumn);
}
}
private void TextBoxSelectedCommandHandler()
{
// TODO: Change SelectedItem of ListView
// this.SelectedGroupIndex = ...;
}
private ListViewString GetListViewString(string text)
{
return new ListViewString { Value = text };
}
private string GetTextFromListViewString(ListViewString listViewString)
{
return listViewString.Value;
}
}
/// <summary>
/// Class used to show user Text in ListView.
/// Using this class fixes the issue that ObservableCollection didn't update
/// after user changed values of TextBoxes in GUI.
/// </summary>
public class ListViewString : DependencyObject
{
public string Value
{
get
{
return (string)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(ListViewString), new PropertyMetadata(string.Empty));
}
View:
<Window.Resources>
<ResourceDictionary>
<local:MainWindowViewModel x:Key="vm" />
</ResourceDictionary>
</Window.Resources>
<Grid Margin="10,10,10,10" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition />
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemsSource="{Binding AllTexts, Source={StaticResource vm}, Mode=TwoWay}"
Background="Blue"
SelectedIndex="{Binding SelectedGroupIndex, Source={StaticResource vm}}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
Width="100" Height="40">
<TextBox.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding TextBoxSelected, Source={StaticResource vm}}" />
</TextBox.InputBindings>
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,20,0,0">
<Button Content="Add Group" Width="120" Height="30"
Command="{Binding AddGroup, Source={StaticResource vm}}" />
<Button Content="Add Column" Margin="20,0,0,0" Width="120" Height="30"
Command="{Binding AddColumn, Source={StaticResource vm}}" />
<TextBlock Width="120" Height="30" FontSize="20" Margin="20,0,0,0"
Text="{Binding SelectedGroupIndex, Source={StaticResource vm}}" />
</StackPanel>
</Grid>
Actually what you need is to pass the DataContext of your TextBox to the command as parameter, so use CommandParameter for it and implement your command with parameter:
<TextBox.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding TextBoxSelected, Source={StaticResource vm}}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Mode=Self}}"/>
</TextBox.InputBindings>
So you will have an item from your items source collection as command parameter and can find the index of it.
EDIT: I discovered that it was in fact the items presenter in my items control within the scroll viewer that wasn't updating correctly rather than the scrollviewer. I added an answer to reflect this.
I have a simple set up for a custom view interaction request. The view contains a scroll viewer but the scroll viewers scrollable height doesn't update if the items control within it has an items source update. The relevant code is below.
Confirmation model:
public class ProfileImportConfirmation : Confirmation
{
public ObservableCollection<ProfileAcceptPair> PossibleProfiles { get; set; } = new ObservableCollection<ProfileAcceptPair>();
public ObservableCollection<Profile> ConfirmedProfiles { get; set; } = new ObservableCollection<Profile>();
}
ViewModel:
public class ProfileImportPopupViewModel : BindableBase, IInteractionRequestAware
{
ProfileImportConfirmation _profileImportConfirmation;
public InteractionRequest<Confirmation> YesNoConfirmationInteractionRequest { get; }
public DelegateCommand AcceptCommand { get; set; }
public DelegateCommand CancelCommand { get; set; }
public ProfileImportPopupViewModel()
{
AcceptCommand = new DelegateCommand(Accept);
CancelCommand = new DelegateCommand(Cancel);
YesNoConfirmationInteractionRequest = new InteractionRequest<Confirmation>();
}
public INotification Notification
{
get { return _profileImportConfirmation; }
set
{
if (value is ProfileImportConfirmation confirmation)
{
_profileImportConfirmation = confirmation;
OnPropertyChanged(nameof(Notification));
}
}
}
public Action FinishInteraction { get; set; }
void Cancel()
{
_profileImportConfirmation.Confirmed = false;
FinishInteraction();
}
void Accept()
{
_profileImportConfirmation.Confirmed = true;
_profileImportConfirmation.ConfirmedProfiles.Clear();
_profileImportConfirmation.ConfirmedProfiles.AddRange(_profileImportConfirmation.PossibleProfiles.Where(p => p.Accepted).Select(p => p.Profile).ToList());
if (_profileImportConfirmation.ConfirmedProfiles.Any(p => p.IsRootProfile))
YesNoConfirmationInteractionRequest.Raise(
new Confirmation
{
Title = DisplayStrings.AreYouSureLabel,
Content = "Proceed?"
},
confirmed => FinishInteraction());
else
{
FinishInteraction();
}
}
}
View:
<UserControl
MaxHeight="500"
MinWidth="400"
d:DataContext="{d:DesignInstance Type=viewModels:ProfileImportPopupViewModel, IsDesignTimeCreatable=False}"
Loaded="ProfileImportPopup_OnLoaded">
<i:Interaction.Triggers>
<mvvm:InteractionRequestTrigger SourceObject="{Binding YesNoConfirmationInteractionRequest, Mode=OneWay}">
<mvvm:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" WindowStyle="{StaticResource PopupWindow}" WindowStartupLocation="CenterOwner">
<mvvm:PopupWindowAction.WindowContent>
<popups:YesNoConfirmationPopup />
</mvvm:PopupWindowAction.WindowContent>
</mvvm:PopupWindowAction>
</mvvm:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Grid Margin="30, 0, 30, 30">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.ColumnSpan="2" Content="{Binding Notification.Title}" HorizontalAlignment="Left" FontFamily="{StaticResource 'Brandon Grotesque Bold'}" FontSize="{StaticResource LargeFontSize}"/>
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Content="{Binding Notification.Content}" HorizontalAlignment="Center" VerticalContentAlignment="Center" FontFamily="{StaticResource 'Brandon Grotesque Bold'}" FontSize="{StaticResource LargeFontSize}"/>
<ScrollViewer x:Name="aoeu" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" CanContentScroll="True" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Notification.PossibleProfiles}" Margin="0, 0, 30, 0">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:ProfileAcceptPair}">
<CheckBox Style="{StaticResource RightAlignedCheckBox}" Content="{Binding Name}" IsChecked="{Binding Accepted}" HorizontalContentAlignment="Right"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Button Grid.Row="3" Grid.Column="0" HorizontalAlignment="Center" Content="{x:Static resources:DisplayStrings.CancelButton}" Style="{StaticResource ModalWindowButton}" Command="{Binding CancelCommand}" Margin="0" VerticalAlignment="Center"/>
<Button Grid.Row="3" Grid.Column="1" Content="{x:Static resources:DisplayStrings.OKButton}" Style="{StaticResource ModalWindowButton}" Command="{Binding AcceptCommand}" Margin="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
It seems the items source is updating fine and I can see the new item element hidden below the scroll viewer but I can't scroll down to it.
How can I get the scrollable height to update?
The problem wasn't with the scroll viewer. It was the items presenter from the items control inside the scroll viewer. It wasn't updating it's height on items changing.
My solution isn't ideal but it worked. I added a loaded event handler for the user control in the code behind. I then named the items control and using that found the items presenter child and called invalidate measure.
void Popup_OnLoaded(object sender, RoutedEventArgs e)
{
var itemsPresenter = (ItemsPresenter) FindChild(MyItemsControl, typeof(ItemsPresenter));
itemsPresenter.InvalidateMeasure();
}
public DependencyObject FindChild(DependencyObject o, Type childType)
{
DependencyObject foundChild = null;
if (o != null)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(o);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(o, i);
if (child.GetType() != childType)
{
foundChild = FindChild(child, childType);
}
else
{
foundChild = child;
break;
}
}
}
return foundChild;
}
I have a ListView with a DataTemplate, inside this templates there are several textblocks and a button. The button has a contextmenu with fixed items. The binding of the listviewitems works fine, but binding a property to the contextmenu does not seem to work.
<ListView x:Name="lv_clients" Margin="0 22 0 0" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Grid.Column="0" Grid.RowSpan="2" Background="{Binding StateColor}">
</Grid>
<TextBlock Grid.Column="1" Text="{Binding DisplayString}" Foreground="Black" Height="20" FontWeight="Bold" Padding="2,2,0,0" />
<Button Click="Button_ListItem_Click" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Anrufen" Name="mn_call" Click="mn_call_Click" DataContext={Binding Number} />
</ContextMenu>
</Button.ContextMenu> ...</Button>
<StackPanel Grid.Column="1" Grid.Row="1">
<TextBlock Text="{Binding State}" Height="20" Padding="2,2,0,0"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstEntry.KDKGRS}" Height="20" FontWeight="Bold" Padding="2,2,2,0" HorizontalAlignment="Left" Foreground="{Binding FirstEntry.ConvertedKGFARBE}" />
<TextBlock Text="{Binding FirstEntry.ADNAMI}" Height="20" Padding="0,2,0,0" HorizontalAlignment="Left" />
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have removed some unnecessary bits of the code (style and columndefinitions) for readability.
The important part is the MenuItem inside the Button. My underlying class has a public string property Number. This should be passed to the menu item. But the DataContext of the MenuItem is always null inside the click event.
I've read something about the contextmenu not beeing part of the visual tree, but I can't wrap my head around it. Could somebody explain the issue with the binding?
Edit Code for the underlying class:
Again removed some unnecessary code for the question
public class PhoneClient
{
public String Name { get; set; }
public String Number { get; set; }
public String Extension { get; set; }
public String DisplayString
{
get
{
return String.IsNullOrEmpty(Name) ? Number : String.Format("{0} ({1})", Name, Extension);
}
}
}
And the binding of the ListBox:
List<PhoneClient> clients = new List<PhoneClient>();
clients = load(); //returns active Clients
lv_clients.ItemsSource = clients;
Bridging the gap
<ContextMenu Tag="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Anrufen" Click="mn_call_Click" Name="mn_call"
DataContext="{Binding Tag.Number, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</ContextMenu>
Handler
private void mn_call_Click(object sender, RoutedEventArgs e)
{
MenuItem currentMenuItem = (MenuItem)sender;
string number = (string)currentMenuItem.DataContext;
// Do Stuff
}
EDIT
MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Margin="10">
<ListView x:Name="lv_clients" Margin="0 22 0 0" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="LightGray" Width="100">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Background="LightGreen">
</Grid>
<TextBlock Text="{Binding DisplayString}" Foreground="Black" Height="20" FontWeight="Bold" Padding="2,2,0,0" />
<Button Click="Button_ListItem_Click" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button.ContextMenu>
<ContextMenu Tag="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show" Click="mn_call_Click" Name="mn_call" DataContext="{Binding Tag.Number, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</ContextMenu>
</Button.ContextMenu> ...
</Button>
<StackPanel Grid.Row="1">
<TextBlock Text="{Binding State}" Height="20" Padding="2,2,0,0"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Foo" Height="20" FontWeight="Bold" Padding="2,2,2,0" HorizontalAlignment="Left" Foreground="Blue" />
<TextBlock Text="Bar" Height="20" Padding="0,2,0,0" HorizontalAlignment="Left" />
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
namespace WpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<PhoneClient> clients = new List<PhoneClient>();
clients.Add(new PhoneClient() { Name = "Kumar", Number = "0101010", Extension = "555", State = "New York" });
clients.Add(new PhoneClient() { Name = "Shanaya", Number = "1010101", Extension = "555", State = "New Jersey" });
clients.Add(new PhoneClient() { Name = "Billy Bob", Number = "6543210", Extension = "555", State = "Single" });
lv_clients.ItemsSource = clients;
}
public class PhoneClient
{
public String Name { get; set; }
public String Number { get; set; }
public String Extension { get; set; }
public String State { get; set; }
public String DisplayString
{
get
{
return String.IsNullOrEmpty(Name) ? Number : String.Format("{0} ({1})", Name, Extension);
}
}
}
private void mn_call_Click(object sender, RoutedEventArgs e)
{
MenuItem currentMenuItem = (MenuItem)sender;
string number = (string)currentMenuItem.DataContext;
MessageBox.Show("Number " + number);
}
private void Button_ListItem_Click(object sender, RoutedEventArgs e)
{
Button currentButton = (Button)sender;
PhoneClient data = (PhoneClient)currentButton.DataContext;
MessageBox.Show(data.Name + " tapped");
}
}
}
I have a wpf with multiple tabs. The application is a step by step learning tool for students. I want the tabs to be locked initially. When the user has entered the correct data in a tab, the next is enabled after pressing the proceed button.
I tried binding the 'isEnabled' property of tabitem and setting it true when the button is pressed in the View model. It doesn't work.
Here's some snippets of my code:
XAML:
<TabItem Header="Step 1"><my:Step1 Loaded="Step1_Loaded" /></TabItem>
<TabItem Header="Step 2"><my:Step2 Loaded="Step2_Loaded" /></TabItem>
<TabItem IsEnabled="{Binding step3Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Step 3"><my:Step3 Loaded="Step3_Loaded" /></TabItem>
Code-behind for the button:
private void proceed2_Click(object sender, RoutedEventArgs e)
{
var vm = DataContext as ViewModel.ValuesCheck;
if (vm != null)
vm.Step2check();
}
View Model:
class ViewModel:INotifyPropertyChanged
{
bool _step3enabled = false;
public bool step3Enabled
{
get { return _step3enabled; }
set { _step3enabled = value; OnPropertyChanged("step3Enabled"); }
}
public void Step2check()
{
this.step3Enabled = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
}
As I can understand, you need some navigation control that presents its content based on some condition. I can suggest you to use a listbox control and manage its ListBoxItems content visibility or something like this.
Xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Controls}" SelectedItem="{Binding CurrentControlContent}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Visibility="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource Converter}}"
BorderBrush="Tomato" BorderThickness="1" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<Button Width="100" Height="30" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
IsHitTestVisible="False" Content="{Binding Path = Content, UpdateSourceTrigger=PropertyChanged}"></Button>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ContentControl Content="{Binding CurrentControlContent.Content}"></ContentControl>
<Button Grid.Row="1" Content="Check" Command="{Binding CheckCommand}"></Button>
</Grid>
ViewModel
private TabAndControlModel _selectedTabControlModel;
private ICommand _checkCommand;
private TabAndControlModel _currentControlContent;
public ObservableCollection<TabAndControlModel> Controls { get; set; }
public TabAndControlModel CurrentControlContent
{
get { return _currentControlContent; }
set
{
_currentControlContent = value;
OnPropertyChanged();
}
}
public ICommand CheckCommand
{
get { return _checkCommand ?? (_checkCommand = new RelayCommand(Check)); }
}
private void Check()
{
var index = Controls.IndexOf(CurrentControlContent);
var nextIndex = index + 1;
if(nextIndex >= Controls.Count) return;
CurrentControlContent = Controls[nextIndex];
CurrentControlContent.TabIsEnabled = true;
}
Model
private bool _tabIsEnabled;
private string _content;
public bool TabIsEnabled
{
get { return _tabIsEnabled; }
set
{
_tabIsEnabled = value;
OnPropertyChanged();
}
}
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged();
}
}
View on running:
And you have to template and style all that stuff...
Regards
What I did is a search function to search for events.
My problem here is that there's a property that fires up when i enter the page and set my ObservableCollection count to 0, but there are events details in my database.
How this should have work is that when i enter on the page there is a relay command that executes and retrieve all data from database and place it into the observable collection and display the event names into a ListBox. It I only when I enter a character then after it detect the events.
Here are my codes:
xaml:
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Loaded">
<Core:InvokeCommandAction Command="{Binding Searchfromhomepage.EventSearch, Mode=OneWay}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<Grid x:Name="LayoutRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<TextBox x:Name="txtSearch" Background="White" Text="{Binding Path=HomePage.TxtEntered, UpdateSourceTrigger=PropertyChanged}" FontSize="30" Height="57" Margin="19,10,19,0" Grid.Row="1" />-->
<TextBox x:Name="txtTest2" Text="{Binding Searchfromhomepage.Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.667,19,0">
<ListBox Background="Black" x:Name="listBox" FontSize="26" Margin="0,10,0,0" ItemsSource="{Binding Searchfromhomepage.FilteredNames, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="txtEventName" TextWrapping="Wrap" Text="{Binding EventName}" Tapped="txtEventName_Tapped" IsTapEnabled="True" Foreground="White" Width="300" Margin="10,15,0,0" Height="55"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
viewmodel codes:
private static ObservableCollection<Event> _searchEventCollection = new ObservableCollection<Event>();
public static ObservableCollection<Event> SearchEventCollection
{
get { return _searchEventCollection; }
set { _searchEventCollection = value; }
}
//search from homepage event section
private RelayCommand _eventSearch;
/// <summary>
/// Gets the EventSearch.
/// </summary>
public RelayCommand EventSearch
{
get
{ return _eventSearch
?? (_eventSearch = new RelayCommand(
async () =>
{
SearchEventCollection.Clear();
var eventList = await App.MobileService.GetTable<Event>().ToListAsync();
foreach (Event ename in eventList)
{
SearchEventCollection.Add(new Event
{
Id = ename.Id,
EventName = ename.EventName,
Date = ename.Date,
Location = ename.Location,
Desc = ename.Desc
});
}
}));
}
}
private string filter;
public String Filter
{
get
{
return this.filter;
}
set
{
this.filter = value;
RaisePropertyChanged("FilteredNames");
}
}
public List<Event> FilteredNames
{
get
{
if (filter == "")
{
return SearchEventCollection.ToList();
}
else
{
return (from name in SearchEventCollection where name.EventName.ToUpper().StartsWith(filter.ToUpper()) select name).ToList();
}
}
}
public searchfromhomepageViewModel()
{
filter = "";
}
in your view model code set up the _eventSearch command like so
public RelayCommand _eventSearch = new RelayCommand(EventSearch);