I am working on a WPF where I need to dynamically generate Checkboxes 16 times.
XAML:
<Checkboxes Height="14" Command="{Binding CheckboxesGen}" Margin="0" Name="checkBox1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center" />
Using above way, It will be inefficient if I write down this Checkboxes 16 times and have individual Button Click Command for them. I would ideally want to generate them 16 times and have one common method in my viewmodel class as follows:
private ICommand mCheckboxesGen;
public ICommand CheckboxesGen
{
get
{
if (mCheckboxesGen== null)
mCheckboxesGen= new DelegateCommand(new Action(mCheckboxesGenExecuted), new Func<bool>(mCheckboxesGenCanExecute));
return mCheckboxesGen;
}
set
{
mCheckboxesGen= value;
}
}
public bool mCheckboxesGenCanExecute()
{
return true;
}
public void mCheckboxesGenExecuted(some INDEX parameter which gives me selected Checkboxes )
{
// Have a common method here which performs operation on each Checkboxes click based on INDEX which determines which Checkboxes I have selected
}
I had faced the same situation in my C++ app. I had done it in my C++ app as follows:
for(int j = 0; j < 16; j ++)
{
m_buttonActiveChannels[j] = new ToggleButton();
addAndMakeVisible(m_buttonActiveChannels[j]);
m_buttonActiveChannels[j]->addButtonListener(this);
}
//Checking which Checkboxes is clicked
unsigned bit = 0x8000;
for(int i = 15; i >= 0; i--)
{
if(0 != (value & bit)) //Value has some hardcoded data
{
m_buttonActiveChannels[i]->setToggleState(true);
}
else
{
m_buttonActiveChannels[i]->setToggleState(false);
}
bit >>= 1;
}
Hence using this generates it 16 times and has one method which performs operation based on index i.
Using a similar approach or any other approach, How can I achieve it in my wpf app? :)
Please help :)
How about something like this?
<ItemsControl ItemsSource="{Binding CollectionOfObjectsThatRepresentYourCheckBox}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Checkbox Content="{Binding DisplayText }" Checked="{Binding Checked}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
you would need to populate your collection on objects on load or when a command was executed, then you could react to items being checked in the model that you create for it..
public class CheckBoxClass
{
public int Index {get; set;}
public string DisplayText {get; set}
private bool _checked;
public bool Checked
{
get { return _checked;}
set {
_checked = value
doSomethingWhenChecked();
}
}
ObservableCollection<CheckBoxClass> CollectionOfObjectsThatRepresentYourCheckBox = SomeMethodThatPopulatesIt();
this is a much cleaner way to do this and instead of generating the controls you will be just binding to a list of your objects that will be represented by a check box.
Define a viewmodel for the checkboxes, this class will have an Index property and the command implementation based on it. Add an ObservableCollection of checkboxes viewmodels to your current viewmodel. In the view add an ItemsControl bound to this collection with a proper ItemTemplate. You can now add as many checkboxes as you want dynamically in the viewmodel.
Related
I am creating a WPF application to act as a front end for a video games library and I'm attempting to mimic the Netflix UI. One of the features in this application is to cycle through images of games to select which game you want to play.
The desired behavior is different than the behavior when arrowing through the items in a ListBox: when you arrow through items in a ListBox, your selection moves up and down. The behavior I'm looking to implement is that as you arrow through the items, the selected item is always at the first position and the items are cycling across the selector. The term for this would be a carousel where the selected item is at index 0.
I've implemented this poorly and to give some context, here's a picture of how my interface currently looks:
My current implementation
To achieve this, I believe what I should do is extend the StackPanel class or maybe implement my own Panel. But details on custom panels are a bit complicated and hard to come by. I want to show what I've done to this point to get this working but I'm very unhappy with these implementations and I'd like to get some advice on what direction I should go for a proper implementation.
Here are some details on what I've tried.
The screenshot above is a result of a GameList class that I created which implements INotifyPropertyChanged and includes properties for 15 different games.
private GameMatch game0;
public GameMatch Game0
{
get { return game0; }
set
{
if (game0 != value)
{
game0 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Game0"));
}
}
}
private GameMatch game1;
public GameMatch Game1
{
get { return game1; }
set
{
if (game1 != value)
{
game1 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Game1"));
}
}
}
// identical code for games 2-10
private GameMatch game11;
public GameMatch Game11
{
get { return game11; }
set
{
if (game11 != value)
{
game11 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Game11"));
}
}
}
private GameMatch game12;
public GameMatch Game12
{
get { return game12; }
set
{
if (game12 != value)
{
game12 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Game12"));
}
}
}
I've laid the images out in my XAML and added enough so that they will run off the edge of the screen:
<StackPanel Name="GameImages" Orientation="Horizontal">
<Border BorderThickness="2" BorderBrush="AntiqueWhite">
<Image Name="Image_Game1" Source="{Binding CurrentGameList.Game1.FrontImage}"/>
</Border>
<Image Source="{Binding CurrentGameList.Game2.FrontImage}"/>
<!-- identical images for games 3-10 -->
<Image Source="{Binding CurrentGameList.Game11.FrontImage}" />
<Image Source="{Binding CurrentGameList.Game12.FrontImage}" />
</StackPanel>
I implemented a ListCycle class which can take any arbitrary list and a count of items that you want to cycle. In case it helps, here's the code for the ListCycle. It takes care of cycling the lists by tracking the index of items in list that should be displayed on screen in a given position.
public class ListCycle<T>
{
// list of games or whatever you want
public List<T> GenericList { get; set; }
// indexes currently available to display
// will cycle around the size of the generic list
public int[] indices;
public ListCycle(List<T> genericList, int activeCycleCount)
{
GenericList = genericList;
indices = new int[activeCycleCount];
InitializeIndices();
}
private void InitializeIndices()
{
if (GenericList != null)
{
int lastIndex = -1;
for (int i = 0; i < indices.Length; i++)
{
indices[i] = GetNextIndex(lastIndex);
lastIndex = indices[i];
}
}
}
private int GetNextIndex(int currentIndex)
{
currentIndex += 1;
if (currentIndex == GenericList.Count)
{
currentIndex = 0;
}
return currentIndex;
}
private int GetPreviousIndex(int currentIndex)
{
currentIndex -= 1;
if (currentIndex == -1)
{
currentIndex = GenericList.Count - 1;
}
return currentIndex;
}
public int GetIndexValue(int index)
{
return indices[index];
}
public T GetItem(int index)
{
return GenericList[indices[index]];
}
public void CycleForward()
{
for (int i = 0; i < indices.Length; i++)
{
if (i + 1 < indices.Length - 1)
{
indices[i] = indices[i + 1];
}
else
{
indices[i] = GetNextIndex(indices[i]);
}
}
}
public void CycleBackward()
{
for (int i = indices.Length - 1; i >= 0; i--)
{
if(i - 1 >= 0)
{
indices[i] = indices[i - 1];
}
else
{
indices[i] = GetPreviousIndex(indices[i]);
}
}
}
}
So when you press right, I cycle forward and reset the game images. When you press left, I cycle backward and reset the game images. The RefreshGames method takes care of updating all of those game properties in my game list.
private void RefreshGames()
{
Game0 = gameCycle.GetItem(0);
Game1 = gameCycle.GetItem(1);
Game2 = gameCycle.GetItem(2);
Game3 = gameCycle.GetItem(3);
Game4 = gameCycle.GetItem(4);
Game5 = gameCycle.GetItem(5);
Game6 = gameCycle.GetItem(6);
Game7 = gameCycle.GetItem(7);
Game8 = gameCycle.GetItem(8);
Game9 = gameCycle.GetItem(9);
Game10 = gameCycle.GetItem(10);
Game11 = gameCycle.GetItem(11);
Game12 = gameCycle.GetItem(12);
}
This approach works but it doesn't work well. It's not dynamic, it doesn't scale well and it doesn't perform all that well. Arrowing through images one at a time performs just fine but trying to quickly move through them, is a bit slow and feels clunky. It's not a very good user experience.
I tried a second approach, using a listbox bound the to my list of games. And to cycle the games to the left, I would remove the first item from my list of games and insert it at the end of the list. To go to the right, I would remove the item at the end of the list and insert it at index 0. This also worked but it didn't perform very well either.
So I'm looking for suggestions on a better way to implement this that would give better performance (smoother scrolling) and be more dynamic (i.e. this approach may not work well on an ultrawide monitor - 12 games may not be enough depending on the widths of the images). I'm not looking for anyone to solve it for me but point me in the right direction as I'm very new to WPF.
My feeling is that I should be extending the stack panel class and changing the way you cycle through the items or maybe creating my own panel. Can anyone confirm if this is the best approach and if so point me to some good resources to help me understand how to create a custom panel that changes the way navigation is done? I've been reading articles on creating custom panels to try to get my head around that process.
Being new to WPF, I want to make sure I'm not going down a rabbit hole or trying to reinvent a wheel that already exists. So the question is whether a custom panel is the right approach to solving this problem?
I believe what I should do is extend the StackPanel class
WPF encourages composition of existing Controls over inheritance; in your case inheriting the StackPanel looks too complicated for your purpose when you could achieve the same with your second approach:
I would remove the first item from my list of games and insert it at the end of the list
This indeed looks more like idiomatic WPF, especially if you try to follow the MVVM design pattern.
or maybe creating my own panel
This is not an easy step especially if you're new to WPF but that would be very interesting for you. That could be a way to go, especially if you internally rely on a StackPanel (composition) instead of inheriting from it.
Example implementation with an ItemsControl
I will use an ItemsControl which can display a collection of data for you (in your case, you have some GameMatch).
First define the data behind the interface, ie a collection of GameMatch. Let's give each GameMatch a name and a variable IsSelected which tells if the game is selected (ie in first position). I'm not showing the INotifyPropertyChanged implementation but it should be there for both properties.
public class GameMatch : INotifyPropertyChanged {
public string Name { get => ...; set => ...; }
public bool IsSelected { get => ...; set => ...; }
}
Your carousel interface is interested in a collection of GameMatch, so let's create an object to model this.
Our graphical interface is gonna bind to the Items property to display the collection of games.
It is also gonna bind to the two commands that are implemented such as to shift the list to the left or to the right. You can use the RelayCommand to create commands. In a nutshell, a Command is simply an action that gets executed and that you can easily refer to from your interface.
public class GameCollection {
// Moves selection to next game
public ICommand SelectNextCommand { get; }
// Moves selection to previous game
public ICommand SelectPreviousCommand { get; }
public ObservableCollection<GameMatch> Items { get; } = new ObservableCollection<GameMatch> {
new GameMatch() { Name = "Game1" },
new GameMatch() { Name = "Game2" },
new GameMatch() { Name = "Game3" },
new GameMatch() { Name = "Game4" },
new GameMatch() { Name = "Game5" },
};
public GameCollection() {
SelectNextCommand = new RelayCommand(() => ShiftLeft());
SelectPreviousCommand = new RelayCommand(() => ShiftRight());
SelectFirstItem();
}
private void SelectFirstItem() {
foreach (var item in Items) {
item.IsSelected = item == Items[0];
}
}
public void ShiftLeft() {
// Moves the first game to the end
var first = Items[0];
Items.RemoveAt(0);
Items.Add(first);
SelectFirstItem();
}
private void ShiftRight() {
// Moves the last game to the beginning
var last = Items[Items.Count - 1];
Items.RemoveAt(Items.Count - 1);
Items.Insert(0, last);
SelectFirstItem();
}
}
The key here is the ObservableCollection class which will tell the view whenever it changes (for example, everytime we move items around inside it) so the view will update to reflect this.
Then, the view (your XAML) should specify how to display the collection of games. We're gonna use an ItemsControl laying out items horizontally:
<StackPanel>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="10" Background="Beige" BorderBrush="Black" Width="150" Height="50">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="true">
<Setter Property="BorderThickness" Value="5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Name}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Previous" Command="{Binding SelectPreviousCommand}"/>
<Button Content="Next" Command="{Binding SelectNextCommand}"/>
</StackPanel>
</StackPanel>
Notice the ItemsControl ItemsSource="{Binding Items}" which tells the ItemsControl to display all the objects in the Items property. The ItemsControl.ItemsPanel part tells to lay them out in an horizontal StackPanel. The ItemsControl.ItemTemplate part explains how each game should be displayed, and the DataTrigger within tells WPF to increase the border thickness for the selected item. Finally, the StackPanel at the bottom displays two Button which call SelectPreviousCommand and SelectLeftCommand in our GameCollection.
Finally, you should set the DataContext of the whole thing to a new GameCollection:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = new GameCollection();
}
}
From there you can customize the UI as you'd like.
Animations and smooth scrolling
That is a whole other topic but you could for example trigger a translation animation of all your items when clicking one of the buttons.
I'll try and point you in the right direction. If you haven't already checked it out, I would try to make your application follow the MVVM pattern. In your case, the ViewModel would have an ObservableCollection of "Games". You would then bind your ItemsControl's source to that collection.
As far as getting the carousel to work, I think the path you will want to go down is creating a custom ItemsControl or ListBox. You can override the styling and create some custom behavior to get the carousel to work how you would like it to.
I can probably help out more if you have a more specific question.
I am struggling with an update to a ComboBox that previously worked. I originally had its ItemsSource bound to a read-only ObservableCollection<char> property in the ViewModel. When the user instigates changes (which is done with mouse strokes, so dozens of times per second in some cases), the get rebuilds the collection from the Model and returns it.
When I changed to my own object in the ObservableCollection, the ComboBox started flickering during updates. I'm not sure what's going wrong. Here's the code that works, starting with the XAML:
<ComboBox ItemsSource='{Binding FromBins}' SelectedValue='{Binding SelectedFromBin, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}' />
ViewModel:
public ObservableCollection<char> FromBins
{
get
{
ObservableCollection<char> tempBins = new ObservableCollection<char>();
foreach (var item in Map.BinCounts)
{
tempBins.Add(item.Key);
}
return tempBins;
}
}
I simply raise a property change with every mouse movement and the interface works as expected (there is some other logic to ensure the SelectedItem is valid).
To make the interface more useful, I decided to add more information to the ComboBox, using my own class:
public class BinItem : IEquatable<BinItem>
{
public char Bin { get; set; }
public SolidColorBrush BinColor { get; set; }
public string BinColorToolTip { get {...} }
public BinItem( char bin )
{
Bin = bin;
BinColor = new SolidColorBrush(BinColors.GetBinColor(bin));
}
public bool Equals(BinItem other)
{
return other.Bin == Bin ? true : false;
}
}
If I swap char out for BinItem in the working code ViewModel I get flickering as the mouse is moved. Here is the updated XAML:
<ComboBox ItemsSource='{Binding FromBins}' SelectedValue='{Binding SelectedFromBin, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}'>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" ToolTip='{Binding BinColorToolTip}'>
<Rectangle Fill='{Binding BinColor}' Width='10' Height='10' HorizontalAlignment='Center' VerticalAlignment='Center' Margin='0,0,4,0' Stroke='#FF747474' />
<TextBlock Text="{Binding Bin}" Width='16' />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I have tried numerous things, including but not limited to:
-Using a List instead of the ObservableCollection but even though the Get fires every time and returns the correct collection of items, the interface does not always update (though the flickering disappears).
-Leaving all possible bins in the items source and adding a Visibility property to the BinItem class that I bound to (couldn't get it to update).
I suspect I am doing something fundamentally wrong, but no amount of searching SO or otherwise has helped thus far. Any help is appreciated.
I was able to solve this using the ideas from Clemens and Chris. Not sure if this is the most elegant solution, but it works as intended with no measurable performance hit.
Instead of replacing the collection with each refresh, I go through the logic of finding out what's changed (with each update there could be an addition AND a removal simultaneously). Code below:
private ObservableCollection<BinItem> _FromBins = new ObservableCollection<BinItem>();
public ObservableCollection<BinItem> FromBins
{
get
{
if (_FromBins.Count > 0)
{
List<char> BinsToRemove = new List<char>();
foreach (var item in _FromBins)
{
if (!Map.BinCounts.ContainsKey(item.Bin))
{
BinsToRemove.Add(item.Bin);
}
}
foreach (var item in BinsToRemove)
{
_FromBins.Remove(new BinItem(item));
}
}
foreach (var item in Map.BinCounts)
{
if (!_FromBins.Contains(new BinItem(item.Key)) && item.Value > 0) {
_FromBins.Add(new BinItem(item.Key));
}
}
return _FromBins;
}
}
Hope this can help someone else too.
I have to create a grid array the size of this array is determined dynamically.
My try to do this is:
int size = 4; //This "size" will be determined dynamically.suppose i got 4 here
Grid[] rowgrid = new Grid[size];
for (int i = 0; i < size; i++)
{
rowgrid[i].RowDefinitions.Add(new RowDefinition());
}
It don't give any error but when i run it gives exception :
The object reference is not set to an instance of an object.
EDIT:
I want to use array because : after intializing before i have to do like this :
rowgrid[0].Opacity=0.1;
rowgrid[1].Opacity=0.3;
rowgrid[2].Opacity=0.5;
If you suggest me not work programatically then i want to inform in avance that i know that well but i am obliged to do this because i am working in already developed project and no more options are there. It would be a big help if some one bring me to come out of this error or any other alternative to achieve this.
If you want a Grid with differents Rows or Columns. You can use UniformGrid, columns and rows properties are binding.
So,
<UniformGrid Name="uniformGrid1" Rows="{Binding NumberOfRows}" Columns="{Binding NumberOfColumns}">
<Button Content="Button1" Grid.Row="0" Grid.Column="0" />
<Button Content="Button2" Grid.Row="0" Grid.Column="2" />
</UniformGrid>
In your code
private int _numberOfRows;
public int NumberOfRows
{
get { return _numberOfRows; }
set { _numberOfRows= value; RaisePropertyChanged("NumberOfRows"); }
}
private int _numberOfColumns;
public int NumberOfColumns
{
get { return _numberOfColumns; }
set { _numberOfColumns= value; RaisePropertyChanged("NumberOfColumns"); }
}
public MainViewModel()
{
NumberOfColumns = 3;
NumberOfRows = 2;
}
I've been looking around but I haven't been able to find anything on this. I am trying to get started making Windows 8.1 apps in C# with Visual Studio 2013 Pro. I want to be able to access multiple elements (particularly buttons or text blocks) in an array because this is more convenient for developing things like board games. For instance, if I were developing tic-tac-toe, I might use a series of buttons like this:
<Grid>
<Button Name="Cell00"/>
<Button Name="Cell01"/>
<Button Name="Cell02"/>
<Button Name="Cell10"/>
<Button Name="Cell11"/>
<Button Name="Cell12"/>
<Button Name="Cell20"/>
<Button Name="Cell21"/>
<Button Name="Cell22"/>
<Grid/>
Now for the function that would check for a win, I would have to check all possible combinations like this is in the code behind:
private bool CheckForWin()
{
if((Cell00 == Cell01) && (Cell01 == Cell02) && isNotBlank(Cell02)) return true;
if((Cell10 == Cell11) && (Cell11 == Cell12) && isNotBlank(Cell12)) return true
...
return false; //if none of the win conditions pass
}
This type of code would be extremely cumbersome. I would like to write it instead in a way that lets me check the array with for loops.
I realize that with tic-tac-toe, it is fairly easy to code it using brute force, but this was the first example that came to my head. Other games like Reversi or Go would not work well like this because of either the sheer size or the fact that pieces placed can change other cells than the one they were placed on.
Any help with this would be greatly appreciated.
This is not the correct way to use WPF. WPF is designed to use data binding....creating and manipulating UI elements directly is bad form. There are more posts/discussion/questions about this than you can imagine and I'll leave you to research them for yourself. In the mean time this is how you use WPF "properly":
First use NuGet to add MVVM lite to your project so that you get the ViewModelBase class and create a view model for a single cell:
public class Cell : ViewModelBase
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; RaisePropertyChanged(() => this.Text); }
}
}
One level up you'll want a main model to encapsulate an array of these, this is where you will typically do all your game logic:
public class MainModel : ViewModelBase
{
private ObservableCollection<Cell> _Cells;
public ObservableCollection<Cell> Cells
{
get { return _Cells; }
set { _Cells = value; RaisePropertyChanged(() => this.Cells); }
}
public MainModel()
{
this.Cells = new ObservableCollection<Cell>(
Enumerable.Range(1, 100)
.Select(i => new Cell { Text = i.ToString() })
);
}
}
Notice that all I'm doing at the moment is creating a 100-element collection of cells. This main view model becomes the one that you assign to your window's data context:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainModel();
}
Now your XAML controls need to bind to this data. ItemsControl is used to render a collection of elements so use one of those and bind it to your array. You want them displayed in a 2D grid so replace the ItemsPanelTemplate with a WrapPanel. Finally add a DataTemplate for your Cell class so that a button gets drawn for each cell:
<Window.Resources>
<DataTemplate DataType="{x:Type local:Cell}">
<Button Width="32" Height="32" Content="{Binding Text}"/>
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Cells}" Width="320" Height="320" HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
That's how you use WPF. Your logic is stored entirely in the view model and it's completely decoupled from the view. Here's what this particular code displays, it should be pretty self-evident how flexible this code is and easy to change:
That's very possible. Simply declare an array variable :
private Button[] _buttonArray;
populate the array once, maybe in constructor :
_buttonArray = new[] {Cell00, Cell01, .... , Cell22};
And all of the buttons are now accessible through _buttonArray.
I have a Zone object that contains
public int Block {get;set;}
I also have a configuration object which contains minimum and maximum Block values, which are 0 and 2 respectively.
I need to display a ComboBox with the range of valid values, but I need to have the selected value bound to Block.
What's the best way for me to do this?
I've been trying the following:
var blocks = new Dictionary<string, int>();
for (int i = _currentZone.Constraints.Block.Min; i <= _currentZone.Constraints.Block.Max; i++)
{
blocks.Add("Block " + i, i);
}
var blocksCombo = new ComboBoxControl(blocks, GetCurrentBlockValue());
with ComboBoxControl defined as
public ComboBoxControl(Dictionary<string, int> comboItems, int? selectedValue)
{
InitializeComponent();
cboItems.ItemsSource = comboItems;
cboItems.SelectedValue = selectedValue;
}
and the XAML defined as
<Grid>
<ComboBox x:Name="cboItems"
SelectionChanged="combo_SelectionChanged"
Height="25"
SelectedValuePath="Value">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
When the combo_SelectionChanged event is triggered I manually update the Block value, which isn't ideal.
What I'd like is to be able to set the combo box with the items in the dictionary, but when I change the selected item the value is bound to a different object - the Block. Is this possible?
If so, how can I implement this? If not, is there a better way for me to go about this than what I'm currently doing?
I believe it's as simple as changing you xaml to have...
<ComboBox x:Name="cboItems"
SelectionChanged="combo_SelectionChanged"
Height="25"
SelectedValuePath="Value"
SelectedItem="{Binding Block}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Assuming the data context is setup correctly, you probably need to set the datacontext of the combobox to your Zone object at some point, maybe pass it along with the constructor...
var blocksCombo = new ComboBoxControl(blocks, GetCurrentBlockValue(), this);
public ComboBoxControl(Dictionary<string, int> comboItems, int? selectedValue, Zone zone)
{
InitializeComponent();
cboItems.ItemsSource = comboItems;
cboItems.SelectedValue = selectedValue;
cboItems.DataContext = zone;
}
edit:
Also I think Henk is right, you might want to change the dictionary to instead be a ObservableCollection of Block. (actually just realized block is just an int, this will probably work as a dictionary)
I hope I understood everything right. You have the combobox and want to bind to one specific zone?
<ComboBox ItemsSource="{Binding ValidValuesList}" ItemStringFormat="Block {0}" SelectedItem="{Binding MyZone.Block}"/>
This binds to
public List<int> ValidValuesList
{
get { return new List<int> { 0, 1, 2 }; }
}
and to
public Zone MyZone { get; set; }
in your usercontrols DataContext.