yet another wpf listbox refresh - wpf

As I am new to wpf i am loosing myself in the webpages about similar topic. I hope somebody could help me explain some basic stuff that I could not manage to understand.
I have a wpf application connected over a websocket to the server. The server returns me every 5 seconds a List. Every new list has nothing to do with the old one. When i get the new list, the old one is not important anymore. The only property of a Player (in the list) that interests me in his ID.
Somehow I need to refresh or update the listbox. I used the observable collection in this way:
private static ObservableCollection<Player> sample;
private static List<Player> sample2 = new List<Player>();
public List<Player> update
{
set
{
sample2 = value;
sample = new ObservableCollection<Player>((List<Player>) sample2);
onPropertyChanged(sample, "ID");
}
}
private void onPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
When debugging the propertychanged is always null. Im really lost here how to update the listbox.
The xaml of the listbox goes like this:
<DataTemplate x:Key="PlayerTemplate">
<WrapPanel>
<Grid >
<Grid.ColumnDefinitions x:Uid="5">
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBlock VerticalAlignment="Center" Margin="5" Grid.Column="0" Text="{Binding Path=ID}" FontSize="22" FontWeight="Bold"/>
</Grid>
</WrapPanel>

sample doesn't have a property called "ID" because sample is your collection, not your Player instance. Moreover, since you are completely replacing the collection, there is no point using an observable one. Try this:
private ICollection<Player> players = new List<Player>();
public ICollection<Player> Players
{
get { return this.players; }
private set
{
this.players = value;
// the collection instance itself has changed (along with the players in it), so we just need to invalidate this property
this.OnPropertyChanged(this, "Players");
}
}

Related

ContentControl visibility binding not working

I have a visibility binding like this inside a UserControl.
<Grid x:Name="_dockPanelMain">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" cal:RegionManager.RegionName="{x:Static Member=consts:RegionNames.MainMenu}" DockPanel.Dock="Top" Visibility="{Binding MainMenuVisibility, Mode=TwoWay}"/>
<ContentControl Grid.Row="0" x:Name="dockManagerModules" cal:RegionManager.RegionName="{x:Static Member=consts:RegionNames.Modules}" Visibility="{Binding ModulesVisibility, Mode=TwoWay}"/>
<ContentControl Grid.Row="1" x:Name="dockManagerStatusBar" cal:RegionManager.RegionName="{x:Static Member=consts:RegionNames.StatusBar}"/>
</Grid>
This user control is given a ViewModel, of which relevant part is
private void SwitchMenuAndModulViews(object sender)
{
if (ModulesVisibility == Visibility.Visible)
{
ModulesVisibility = Visibility.Collapsed;
MainMenuVisibility = Visibility.Visible;
}
else
{
ModulesVisibility = Visibility.Visible;
MainMenuVisibility = Visibility.Collapsed;
}
}
private Visibility _modulesVisibility = Visibility.Visible;
public Visibility ModulesVisibility
{
get { return _modulesVisibility; }
set
{
_modulesVisibility = value;
RaisePropertyChangedEvent(() => ModulesVisibility);
}
}
private Visibility _mainMenuVisibility = Visibility.Collapsed;
public Visibility MainMenuVisibility
{
get { return _mainMenuVisibility; }
set
{
_mainMenuVisibility = value;
RaisePropertyChangedEvent(() => MainMenuVisibility);
}
}
When SwitchMenuAndModulView gets called, nothing happens. The variables change, but the binding does not work and both ContentControls have Visibility set as Visible at all times, which I guess is the default and none of them ever change to Collapsed.
SOLVED:
I solved it myself, the problem was not in the binding itself but in the way the application dealt with instantiating and showing the UserControl.
I had an instance of a UserControl A that had this UserControl B as a part of its content. Naturaly, A created an instance of B upon its creation.
Later, I replaced the instance of B in A by a different instance of B (the one with my viewModel) but I probably forgot to tell the visual tree about the change, therefore visualy it still showed the old B instance whose binding did not lead anywhere.
I kind of wish an application threw an exception when a blind binding happens (as in, it binds to an element that is either not instantiated or misspelled) instead of just leaving the default there. This behaviour of XAML generates so many bugs.

Add items to ItemsControl

I have an application for telephone call. Each call(line) has its own unique information. Say a colorful icon plus and line number. The numbers are in a queue, I used parallel programming skills to deal these items. When processing the item, the information is shown on the screen. Here I prefer ItemsControl.
The expected result likes the image. I want to
I borrowed the code for phone icon.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<SolidColorBrush x:Key="RedBrush" Color="Red" />
<SolidColorBrush x:Key="AmberBrush" Color="#FFFFC500" />
<SolidColorBrush x:Key="GreenBrush" Color="Green" />
<Geometry x:Key="PhoneIcon">F1M52.5221,11.1016C52.0637,10.6796 44.4973,4.55737 29.4347,12.7369 26.3098,14.4296 23,17.224 20.095,20.1692 17.1497,23.073 14.3555,26.3842 12.6626,29.509 4.48303,44.5703 10.6093,52.1393 11.0298,52.5962 11.0298,52.5962 12.9778,55.5885 14.7057,53.8579L23.3555,45.2134C24.897,43.6692 22.7721,39.2134 18.2563,39.3541 17.1301,39.3906 15.5481,38.9531 17.0571,35.5156 18.3945,32.4623 22.3436,27.4766 24.8879,24.9648 27.4048,22.418 32.3904,18.4713 35.4426,17.1301 38.8787,15.6223 39.3175,17.2031 39.2811,18.332 39.1393,22.8462 43.5962,24.9686 45.1366,23.4283L53.7864,14.7787C55.5142,13.052,52.5221,11.1016,52.5221,11.1016z</Geometry>
My question: If I know the color of the phone icon and the phone number, how to add it to the itemscontrol? The phone number needs to be bind, I assume I have a class:
public class Lines
{
public string color { get; set; }
public string linenumber { get; set; }
}
And I defined the ItemsControls as:
<DockPanel>
<ItemsControl Height="300">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
Not sure the next step?
Assuming you are not using MVVM here, just XAML forms with code behind classes - you need to alter your code behind first a little. You need an object to represent a "line" - and make the colour a brush, not a string:
public class Line
{
public string LineNumber { get; set; }
public System.Windows.Media.Brush LineColour { get; set; }
}
Then you need code to build a collection of these which your ItemsControl can display. It might look something like this:
public partial class MainWindow : Window
{
private List<Line> _lines;
public MainWindow()
{
InitializeComponent();
_lines = new List<Line>();
// you can swap this for iterating around a database query or whatever you use to store the lines / calls
_lines.Add(new Line() { LineNumber = "line1", LineColour = new SolidColorBrush(Colors.Red) });
_lines.Add(new Line() { LineNumber = "line2", LineColour = new SolidColorBrush(Colors.Green) });
_lines.Add(new Line() { LineNumber = "line3", LineColour = new SolidColorBrush(Color.FromRgb(255, 188, 59)) });
// this part binds this list to your itemsControl
items.ItemsSource = _lines;
}
Then your XAML is fairly easy, you just define an ItemsControl called "items" and define a style which will be applied to each item. This will contain the line number apply the brush to the image, which you will make a "Path":
<!-- this bit defines how each item looks-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Path Grid.Column="0" Fill="{Binding LineColour}" Data="F1M52.5221,11.1016C52.0637,10.6796 44.4973,4.55737 29.4347,12.7369 26.3098,14.4296 23,17.224 20.095,20.1692 17.1497,23.073 14.3555,26.3842 12.6626,29.509 4.48303,44.5703 10.6093,52.1393 11.0298,52.5962 11.0298,52.5962 12.9778,55.5885 14.7057,53.8579L23.3555,45.2134C24.897,43.6692 22.7721,39.2134 18.2563,39.3541 17.1301,39.3906 15.5481,38.9531 17.0571,35.5156 18.3945,32.4623 22.3436,27.4766 24.8879,24.9648 27.4048,22.418 32.3904,18.4713 35.4426,17.1301 38.8787,15.6223 39.3175,17.2031 39.2811,18.332 39.1393,22.8462 43.5962,24.9686 45.1366,23.4283L53.7864,14.7787C55.5142,13.052,52.5221,11.1016,52.5221,11.1016z"/>
<TextBlock Grid.Column="1" Text="{Binding LineNumber}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- this bit defines the list appearance, we'll go vertical in a stack panel! -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
If you are using MVVM, everything still applies, but instead of assigning the ItemsSource in code behind, you would bind to the _lines collection of your view model - and you would make that an ObservableCollection rather than List if you wanted to add/remove dynamically.
But I have given you the simplest case as that's what your original question suggests!

I am not able to set DataContext from code behind of one userControl to another userControl

I am not able to set DataContext from code behind of one userControl to another userControl .
private void grdWorkingList_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) {
DashboardSynopsisViewModel dsViewModel = new DashboardSynopsisViewModel();
AuditInfoViewModel auditInfoViewModel = new AuditInfoViewModel();
AuditInfoView auditInfoView = new AuditInfoView();
var selectedItem = (grdWorkingList.SelectedItem as AutoMgmtSoln.AuditWinPro.ClientData.Model.AuditDTO);
// MainWindow mainWindow = new MainWindow();
DSViewContentControl.Content = new AuditInfoView();
auditInfoView.DataContext = auditInfoViewModel;
auditInfoViewModel.AuditDTO = auditInfoViewModel.getAuditById(selectedItem.AuditId);
}
I have two user controls DashboardSynopsisView and AuditInfoView having view models DashboardSynopsisViewModel and AuditInfoViewModel. So in the code behind I have a event grdWorkingList_MouseDoubleClick which is fired on mouseDouble click which sets the content control of the dashboardSynopsisView to the AuditInfoView along with it's DataContext to AuditInfoViewModel.
AuditInfoViewModel has a property AuditDTO which I am using to display information of the selected item.
Here is part of my .xaml file
<TextBlock Grid.Column="0" Grid.Row="0" Text="Company Code :"></TextBlock>
<TextBlock Grid.Column="1" Grid.Row="0" Width="auto" Text="{Binding AuditDTO.CompanyCode}" ></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" >Company Name :</TextBlock>
<TextBlock Grid.Column="1" Grid.Row="1" Width="auto" Text="{Binding AuditDTO.CompanyName}" ></TextBlock>
Here is the change I have made to resolve my the problem .
private void grdWorkingList_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DashboardSynopsisViewModel dsViewModel = new DashboardSynopsisViewModel();
AuditInfoViewModel auditInfoViewModel = new AuditInfoViewModel();
AuditInfoView auditInfoView = new AuditInfoView();
var selectedItem = (grdWorkingList.SelectedItem as AutoMgmtSoln.AuditWinPro.ClientData.Model.AuditDTO);
// MainWindow mainWindow = new MainWindow();
DSViewContentControl.Content = ***auditInfoView***;
auditInfoView.DataContext = auditInfoViewModel;
auditInfoViewModel.AuditDTO = auditInfoViewModel.getAuditById(selectedItem.AuditId);
}
You are setting the "auditInfoView" variable's data context, but setting the content control to a new AuditInfo view.
Make these match (probably by changing the content set to the auditInfoView variable) and your code should work.
To provide a little background, in case you are new to C#, using the new operator causes the class's constructor to be invoked, which creates a new object with default values (plus any sets or operations done by the constructor itself). Setting another instance's DataContext property will have no effect on the newly constructed instance.

Get .Text property from dynamically added Textbox

I am dynamically adding several textboxes to my grid in my code behind. I would like to be able to capture what the user enters into those textboxes.
I'm not quiet sure how to do this as the name of the dynamically added textbox is not available when I try to add it in my codebehind.
I want to create a querybuilder tool. This is very rudementary but basically I want to add multiple comboboxes, textboxes and buttons.
First of all, you must leave behind the traditional mentality of manipulating UI elements in code and Embrace MVVM
<Window x:Class="MiscSamples.QueryBuilderSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MiscSamples"
Title="QueryBuilderSample" Height="300" Width="300">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding Operators}"
SelectedItem="{Binding Operator}"/>
<TextBox Text="{Binding Value}" Grid.Column="1"/>
<Button Content="Add" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Code behind:
public partial class QueryBuilderSample : Window
{
public List<QueryCriteria> Criterias { get; set; }
public QueryBuilderSample()
{
InitializeComponent();
DataContext = Criterias = Enumerable.Range(0, 10).Select(x => new QueryCriteria()).ToList();
}
}
ViewModel:
public class QueryCriteria
{
public List<Operators> Operators
{
get
{
return Enum.GetValues(typeof(Operators))
.Cast<Operators>()
.ToList();
}
}
public Operators Operator { get; set; }
public string Value { get; set; }
}
public enum Operators
{
Equals,
Contains,
GreaterThan,
SmallerThan,
}
Result:
Notice that I'm not doing a single line of code to create / manipulate UI elements. Everything is done via DataBinding.
Simple code. No complex event handling stuff or anything like that.
Declarative Code. Just Simple, Simple Properties and INotifyPropertyChanged. That's the default approach to EVERYTHING in WPF.
When you insert a text box, keep a reference to it in some object. A dictionary could be a good choice. That way, you can get that reference later, and from that reference you can read its Text property.
#jeff V you can simply capture the textbox text by using the name your assigning those textboxes..
you are using tb1,tb2 as textboxes name...so you can easily get the values using

WPF binding subcontrols to a parent control

I am trying to perform databinding, given the following case:
I have a class called "Node" that has two properties: "speed" and "pauseTime". I then have an array of Nodes.
In my XAML, I have a numeric control (labeled Node) that allows the user to switch between Nodes. There are also two subcontrols which I want to have show speed and pauseTime for the selected Node.
My question is how do I set the databinding for speed, for example, so that it shows the speed for the selected Node in the Nodes array based on the value in the Node numeric control?
I tried googling this, but am not sure what search terms to use.
I'm not sure what you mean by numeric control, but I knocked up an example of a master details control scenario.
In the XAML you have a Grid with a ListView (the master), and a TextBlock (the details):
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding}" x:Name="masterListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Grid.Row="1" Text="{Binding ElementName=masterListView, Path=SelectedItem.Speed}" />
</Grid>
The code behind looks like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new Node[]
{
new Node() { Speed = 1, PauseTime = "1 Min", Name = "Item 1" },
new Node() { Speed = 2, PauseTime = "2 Mins" , Name = "Item 2" }
};
}
}
public class Node
{
public int Speed { get; set; }
public string PauseTime { get; set; }
public string Name { get; set; }
}
The child TextBlock binds to the Speed property of the Selected node. If you add IsSynchronizedWithCurrentItem="True" to the ListView, the first item will automatically be selected when the window is loaded. If you Google master details/WPF and IsSynchronizedWithCurrentItem="True" you will find more details.
It would probably also be useful to Google how to do this using MVVM- a reasonable approach is mentioned in this Stackoverflow Answer.

Resources