I've merged two programs, and now there are two DataContexts in my code. One is DataContext = this; and the other is DataContext = _uiData;. I'd like to keep the former, because there are many bindings relying on this DataContext (not shown in my code, though). For the latter, I changed it to textBox.Text = _uiData.ToString();, but it doesn't show anything. How would you take care of this kind of situation?
Here is my code:
MainWindow.xaml.cs
using Both_ListBox_n_TextBox_Binding.ViewModel;
using System.Collections.ObjectModel;
using System.Windows;
namespace Both_ListBox_n_TextBox_Binding
{
public partial class MainWindow : Window
{
public ObservableCollection<GraphViewModel> RightListBoxItems { get; }
= new ObservableCollection<GraphViewModel>();
private UISimpleData _uiData = new UISimpleData();
public MainWindow()
{
InitializeComponent();
RightListBoxItems.Add(new GraphViewModel("T1"));
RightListBoxItems.Add(new GraphViewModel("T2"));
RightListBoxItems.Add(new GraphViewModel("T3"));
DataContext = _uiData; // How can I show this?
//textBox.Text = _uiData.ToString(); // This doesn't show anything
DataContext = this; // I'd like to KEEP this
//RightListBox.ItemsSource = RightListBoxItems; // Works, but not for this time
}
}
}
MainWindow.xaml
<Window x:Class="Both_ListBox_n_TextBox_Binding.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:Both_ListBox_n_TextBox_Binding"
mc:Ignorable="d"
Title="MainWindow" Height="253.5" Width="297.5">
<Grid>
<ListBox x:Name="RightListBox"
ItemsSource="{Binding RightListBoxItems}" DisplayMemberPath="Text"
SelectionMode="Extended" Margin="20,20,20,100"/>
<TextBox Margin="100,0,0,40" HorizontalAlignment="Left" VerticalAlignment="Bottom" Height="22" Width="100"
Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding (Validation.Errors)[0].ErrorContent,
RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</Window>
ViewModel/UISimpleData.cs
using System;
using System.ComponentModel;
namespace Both_ListBox_n_TextBox_Binding.ViewModel
{
public class UISimpleData : INotifyPropertyChanged, IDataErrorInfo
{
private double _doubleField = 5.5;
public double DoubleField
{
get
{
return _doubleField;
}
set
{
if (_doubleField == value)
return;
_doubleField = value;
RaisePropertyChanged("DoubleField");
}
}
public string this[string propertyName]
{
get
{
string validationResult = null;
switch (propertyName)
{
case "DoubleField":
{
if (DoubleField < 0.0 || DoubleField > 10.0)
validationResult = "DoubleField is out of range";
break;
}
default:
throw new ApplicationException("Unknown Property being validated on UIData");
}
return validationResult;
}
}
public string Error { get { return "Not Implemented"; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
ViewModel/GraphViewModel.cs
namespace Both_ListBox_n_TextBox_Binding.ViewModel
{
public class GraphViewModel
{
public string Text { get; }
public GraphViewModel(string text) => Text = text;
}
}
Thank you in advance.
I have two windows in my project(MainWindow and one small window for some properties of controls of the MainWindow). In one Tab in the MainWindow there is a Grid divided to ten Columns. In each Column there are some Controls. Below is a sample code of my project.
I want if I check the Period(CheckBox in PropertiesWindow) the Label(MainWindow) to be "Period" and when I check the Frequency(CheckBox in PropertiesWindow) the Label(MainWindow) to be "Frequency".
I want, when I check one of the checkboxes at the PropertiesWindow (Period or Frequency), the Label (lb_freq1) at the MainWindow to change its Content according to the Content of the checked CheckBox. (Moreover, the selected units to be displayed at the time_div1(Label)).
FIRST SOLUTION:
XAML MainWindow:
<Window x:Class="wpf1.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-wpf1"
mc:Ignorable="d"
Title="wpf1" Height="720" Width="1280" WindowStartupLocation="CenterScreen" Icon="kkk.bmp" Background="#FFE0E0E0" Foreground="#FF49A81D" BorderBrush="#FFB93838" >
<Grid>
<TabControl x:Name="tabControl">
<TabItem Header="Tab1">
<Grid>
<StackPanel>
<Label x:Name="lb_freq1" Content="Period" HorizontalAlignment="Center" Margin="0,10,0,0" />
<StackPanel Orientation="Horizontal" Margin="0" HorizontalAlignment="Center">
<TextBox x:Name="txt_freq1" Width="50" Height="20" HorizontalContentAlignment="Right" BorderThickness="1,1,0,1" HorizontalAlignment="Left" VerticalContentAlignment="Center"/>
<Label x:Name="time_div1" Content="us" Width="20" BorderThickness="0,1,1,1" Margin="0" HorizontalAlignment="Right" Height="20" Padding="0" BorderBrush="#FFABADB3" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
</StackPanel>
<Label x:Name="lb_width1" Content="Pulse Width" HorizontalAlignment="Center" Margin="0,10,0,0" />
<StackPanel Orientation="Horizontal" Margin="0" HorizontalAlignment="Center">
<TextBox x:Name="txt_width1" Width="50" Height="20" HorizontalContentAlignment="Right" BorderThickness="1,1,0,1" HorizontalAlignment="Left"/>
<Label x:Name="pv_div1" Content="us" Width="20" BorderThickness="0,1,1,1" Margin="0" HorizontalAlignment="Right" Height="20" Padding="0" BorderBrush="#FFABADB3" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" />
</StackPanel>
<Button x:Name="Properties1" Content="Properties" Margin="10,30,10,10" HorizontalAlignment="Center" BorderBrush="Blue" Click="Properties1_Click" />
</StackPanel>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
EDITED
Code Behind MainWindow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Threading;
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.Navigation;
using System.Windows.Shapes;
using System.Globalization;
using System.ComponentModel;
namespace wpf1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Properties1_Click(object sender, RoutedEventArgs e)
{
string res1 = lb_freq1.Content.ToString();
string res3 = time_div1.Content.ToString();
var newWindow = new PWMProperties();
newWindow.Owner = this;
newWindow.ShowDialog();
string result1 = newWindow.Value1;
if (result1 == null)
{
lb_freq1.Content = res1;
}
else
{
lb_freq1.Content = result1;
}
string result3 = newWindow.Unit1;
if (result3 == null)
{
time_div1.Content = res3;
}
else
{
time_div1.Content = result3;
}
}
}
}
XAML PropertiesWindow:
<Window x:Class="wpf1.PWMProperties"
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:wpf1"
mc:Ignorable="d"
Title="Properties" Height="335" Width="285" ResizeMode="NoResize" BorderThickness="0" WindowStartupLocation="CenterOwner">
<Window.Resources>
<local:BoolConverter2 x:Key="Converter"></local:BoolConverter2>
<local:BoolConverter x:Key="Reverse"></local:BoolConverter>
</Window.Resources>
<Grid>
<StackPanel VerticalAlignment="Top" Margin="0,10,0,0" HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal" Margin="0,0,0,20">
<StackPanel Margin="0,0,49,0">
<RadioButton x:Name="SelectPeriod" Content="Period" Margin="0,0,0,0" Click="SelectPeriod_Click" />
<ComboBox x:Name="PeriodUnits" Padding="3,2,2,2" IsReadOnly="True" IsEditable="True" Text="us" IsEnabled="{Binding ElementName=SelectPeriod, Path=IsChecked}" SelectionChanged="PeriodUnits_SelectionChanged"
ItemsSource="{Binding PeriodComboBoxItems}">
</ComboBox>
</StackPanel>
<StackPanel Margin="20,0,0,0">
<RadioButton x:Name="SelectFrequency" Content="Frequency" Click="SelectFrequency_Click" />
<ComboBox x:Name="FrequencyUnits" Padding="3,2,2,2" IsReadOnly="True" IsEditable="True" Text="Hz" IsEnabled="{Binding ElementName=SelectFrequency, Path=IsChecked}" SelectionChanged="FrequencyUnits_SelectionChanged"
ItemsSource="{Binding FrequencyComboBoxItems}">
</ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Margin="0">
<Button x:Name="OkButton" Content="OK" Margin="135,5,10,5" Click="OkButton_Click" Width="60" />
<Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="60" Margin="0,5,10,5" />
</StackPanel>
</Grid>
</Window>
EDITED
Code Behind PropertiesWindow:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.ComponentModel;
using System.IO;
using System.Xml;
using System.Windows.Markup;
namespace wpf1
{
/// <summary>
/// Interaction logic for Analog.xaml
/// </summary>
public partial class PWMProperties : Window
{
public ObservableCollection<string> PeriodComboBoxItems { get; set; }
public ObservableCollection<string> FrequencyComboBoxItems { get; set; }
public ObservableCollection<string> PulseWidthComboBoxItems { get; set;}
public PWMProperties()
{
InitializeComponent();
this.DataContext = this;
this.PeriodComboBoxItems = new ObservableCollection<string>() { "us", "ms", "s" };
this.FrequencyComboBoxItems = new ObservableCollection<string>() { "Hz", "kHz", "MHz" };
this.PulseWidthComboBoxItems = new ObservableCollection<string>() { "us", "ms", "s" };
}
string val1, val2, unit1, unit2;
private void OkButton_Click(object sender, RoutedEventArgs e)
{
if (SelectPeriod.IsChecked == true)
{
val1 = "Period";
if (unit1 == null || unit1!="ms") unit1 = "us";
}
if (SelectFrequency.IsChecked == true)
{
val1 = "Frequency";
if (unit1 == null || unit1!="kHz") unit1 = "Hz";
}
DialogResult = true;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
}
public string Value1 { get { return val1; } }
public string Value2 { get { return val2; } }
public string Unit1 { get { return unit1; } }
public string Unit2 { get { return unit2; } }
private void PeriodUnits_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
unit1 = PeriodUnits.SelectedItem.ToString();
}
private void FrequencyUnits_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
unit1 = FrequencyUnits.SelectedItem.ToString();
}
}
public class BoolConverter2 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool v = (bool)value;
return v;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotSupportedException();
}
}
}
Second possible solution:
Then, I've added to my MainWindow code a ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private string _value1 = "Period";
public event PropertyChangedEventHandler PropertyChanged;
public string Value1
{
get { return _value1; }
set
{
_value1 = value;
OnPropertyChanged("Value1");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
So, most of the code behind of the PropertiesWindow is deleted and I use Binding for the change of lb_freq1(Label) at the MainWindow.
<Label x:Name="lb_freq1" Content="{Binding Value1, Mode=TwoWay}" HorizontalAlignment="Center" Margin="0,10,0,0" />
I don't know how to continue from that state. I'm new to WPF and C#, and I would be thankful if someone could help me in any way.
MAIN ISSUE
I've edited my first solution, so if someone could take a look. What I managed to do now is almost what i want. But, there is a problem. I want, when I click on the OK.Button, the "settings" I made at Properties.Window should change the Labels at MainWindow. Although, when I click on the Cancel.Button or the Close.Button at the upper right corner, any changes made at the Properties.Window should not change the Labels at MainWindow.
Moreover, when I close the Properties.Window, and then open it again for a second time, the CheckBoxes and ComboBox.SelectedItems need to have the same state they had when the Properties.Window closed. But that doesn't happen.
You should set the DataContext of the two windows properly. Currently, you are setting and overriding it in multiple places. Here is my suggestions:
Use ElementName in your Binding in your windows, i.e., wpf1.PWMProperties and wpf1.MainWindow, whenever you want to Bind to a property in them. In other words, give them a name and Bind to them. For example:
<Window x:Class="wpf1.PWMProperties"
.....
Name="owner">
.....
<StackPanel Margin="0,0,49,0">
.....
ItemsSource="{Binding Path=PeriodComboBoxItems, ElementName=owner}">
</StackPanel>
use DataContext for Binding to Value1 in your ViewModel. Set the DataContext of both Windows to be the instance of your ViewModel:
public partial class MainWindow : Window
{
ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
private void Properties1_Click(object sender, RoutedEventArgs e)
{
var newWindow = new PWMProperties();
newWindow.Owner = this;
newWindow.DataContext = viewModel;
newWindow.Show();
}
}
Looking for some advice on how I can achieve the following:
I'm currently building an C# wpf application and i want to add where the application on start up check if a specific usb is attached any of the usb ports
and if not the application cant be interacted till said usb is attached?
any ideas on how this can be done?
We can use the class System.IO.DriveInfo to retrieve all the drives on the system and look for drives where the DriveType is Removable. In addition, the removable drive (USB usually) must be ready, which is accessible as the property IsReady.
First off, we define a provider to retrieve the removable drives:
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace TestPopWpfWindow
{
public static class UsbDriveListProvider
{
public static IEnumerable<DriveInfo> GetAllRemovableDrives()
{
var driveInfos = DriveInfo.GetDrives().AsEnumerable();
driveInfos = driveInfos.Where(drive => drive.DriveType == DriveType.Removable);
return driveInfos;
}
}
}
Let us use the MVVM pattern also, so we define a ViewModelbase class, implementing INotifyPropertyChanged.
using System.ComponentModel;
namespace TestPopWpfWindow
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
It is also handy to have an implemetation of ICommand:
using System;
using System.Windows.Input;
namespace TestPopWpfWindow
{
public class RelayCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
We also set the DataContext of MainWindow to an instance of a demo view model defined afterwards:
namespace TestPopWpfWindow
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new UsbDriveInfoDemoViewModel();
}
}
}
We then define the view model itself and use System.Management.ManagementEventWatcher to look for changes in the drives mounted onto the system.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Windows;
using System.Windows.Input;
namespace TestPopWpfWindow
{
public class UsbDriveInfoDemoViewModel : ViewModelBase, IDisposable
{
public UsbDriveInfoDemoViewModel()
{
DriveInfos = new List<DriveInfo>();
ReloadDriveInfos();
RegisterManagementEventWatching();
TargetUsbDrive = #"E:\";
AccessCommand = new RelayCommand(x => true, x => MessageBox.Show("Functionality executed."));
}
public int UsbDriveCount { get; set; }
private string _targetUsbDrive;
public string TargetUsbDrive
{
get { return _targetUsbDrive; }
set
{
if (_targetUsbDrive != value)
{
_targetUsbDrive = value;
RaisePropertyChanged("TargetUsbDrive");
RaisePropertyChanged("DriveInfo");
}
}
}
public ICommand AccessCommand { get; set; }
private void ReloadDriveInfos()
{
var usbDrives = UsbDriveListProvider.GetAllRemovableDrives();
Application.Current.Dispatcher.Invoke(() =>
{
DriveInfos.Clear();
foreach (var usbDrive in usbDrives)
{
DriveInfos.Add(usbDrive);
}
UsbDriveCount = DriveInfos.Count;
RaisePropertyChanged("UsbDriveCount");
RaisePropertyChanged("DriveInfos");
});
}
public List<DriveInfo> DriveInfos { get; set; }
private ManagementEventWatcher _watcher;
private void RegisterManagementEventWatching()
{
_watcher = new ManagementEventWatcher();
var query = new WqlEventQuery("SELECT * FROM Win32_VolumeChangeEvent");
_watcher.EventArrived += watcher_EventArrived;
_watcher.Query = query;
_watcher.Start();
}
private void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
Debug.WriteLine(e.NewEvent);
ReloadDriveInfos();
}
public void Dispose()
{
if (_watcher != null)
{
_watcher.Stop();
_watcher.EventArrived -= watcher_EventArrived;
}
}
}
}
We also define a WPF multi-converter next to enable the button:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Data;
namespace TestPopWpfWindow
{
public class UsbDriveAvailableEnablerConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Count() != 2)
return false;
var driveInfos = values[1] as List<DriveInfo>;
var targetDrive = values[0] as string;
if (driveInfos == null || !driveInfos.Any() || string.IsNullOrEmpty(targetDrive))
return false;
return driveInfos.Any(d => d.IsReady && d.Name == targetDrive);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And we define a GUI to test this code:
<Window x:Class="TestPopWpfWindow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestPopWpfWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="usbLabel" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding IsReady}" Value="False">
<Setter Property="Background" Value="Gray"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsReady}" Value="True">
<Setter Property="Background" Value="Green"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<local:UsbDriveAvailableEnablerConverter x:Key="usbDriveAvailableEnablerConverter"></local:UsbDriveAvailableEnablerConverter>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="USB Drive-detector" FontWeight="DemiBold" HorizontalAlignment="Center" FontSize="14" Margin="2"></TextBlock>
<TextBlock Text="Removable drives on the system" FontWeight="Normal" HorizontalAlignment="Center" Margin="2"></TextBlock>
<TextBlock Text="Drives detected:" FontWeight="Normal" HorizontalAlignment="Center" Margin="2"></TextBlock>
<TextBlock Text="{Binding UsbDriveCount, UpdateSourceTrigger=PropertyChanged}" FontWeight="Normal" HorizontalAlignment="Center" Margin="2"></TextBlock>
<ItemsControl Grid.Row="0" ItemsSource="{Binding DriveInfos, UpdateSourceTrigger=PropertyChanged}"
Width="100" BorderBrush="Black" BorderThickness="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Label Style="{StaticResource usbLabel}" Width="32" Height="32" FontSize="18" Foreground="White" Content="{Binding Name}">
</Label>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<Button Grid.Row="1" Height="24" Width="130" VerticalAlignment="Top" Margin="10" Content="Access functionality" Command="{Binding AccessCommand}">
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource usbDriveAvailableEnablerConverter}">
<MultiBinding.Bindings>
<Binding Path="TargetUsbDrive"></Binding>
<Binding Path="DriveInfos"></Binding>
</MultiBinding.Bindings>
</MultiBinding>
</Button.IsEnabled>
</Button>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Margin="2" Text="Target this USB-drive:"></TextBlock>
<TextBox Margin="2" Text="{Binding TargetUsbDrive, UpdateSourceTrigger=LostFocus}" Width="100"></TextBox>
</StackPanel>
</Grid>
</Window>
I have now provided the Visual Studio 2013 solution with the code above available for download here:
https://onedrive.live.com/redir?resid=8EF5059044F781FC!40934&authkey=!AAkLBdsXTlMMTy4&ithint=file%2czip
Here's a post explaining how to get a list of all currently connected USB devices.
Get List of connected USB Devices
I have a task of programming a simple program-demonstration of the Lee Algorithm for pathfinding in a maze. I want to make it somewhat graphically interactive: create a 2D table with a variable amount of rows and columns, which's cells can be clicked (and the position of the clicked cell should be able to be tracked). This is because I want to let the user draw the maze obstacles, set the start point etc. What would be the best graphical component that could help me do this and how can I interact with its' cells?
Following on from your comment I would say that WPF is a more natural candidate for this task because it has been designed to do custom layout stuff. Here is a code example I've put together using an items control with a uniform grid that displays a grid of cells - clicking on a cell selects it, clicking again deselects it.
This isn't great WPF, but it might give you some ideas and get you started.
Application:
XAML:
<Window x:Class="LeeAlgorithm.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="380" Width="350">
<Grid>
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="18"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataTemplate.Resources>
<Grid Width="30" Height="30" Margin="2">
<DockPanel ZIndex="9999">
<!-- This is a hack to capture the mouse click in the area of the item -->
<Rectangle
Fill="Transparent"
DockPanel.Dock="Top"
MouseDown="UIElement_OnMouseDown"
Tag="{Binding}" />
</DockPanel>
<Grid>
<Rectangle Fill="{Binding Path=Color}" Stroke="Red" StrokeDashArray="1 2" />
<TextBlock Margin="3,3,3,0" Text="{Binding Path=CellNumber}"/>
</Grid>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Code Behind (your MainWindow.xaml.cs code):
namespace LeeAlgorithm
{
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.Cells = new ObservableCollection<Cell>();
for (int i = 0; i != 50; ++i)
{
this.Cells.Add(new Cell { CellNumber = i });
}
InitializeComponent();
DataContext = this;
}
public ObservableCollection<Cell> Cells { get; private set; }
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var cell = (Cell)((Rectangle)sender).Tag;
if (!cell.IsSelected)
{
cell.Color = new SolidColorBrush(Colors.HotPink);
cell.IsSelected = true;
}
else
{
cell.Color = new SolidColorBrush(Colors.Silver);
cell.IsSelected = false;
}
}
}
public class Cell : INotifyPropertyChanged
{
private int cellNumber;
private SolidColorBrush color = new SolidColorBrush(Colors.Silver);
public event PropertyChangedEventHandler PropertyChanged;
public int CellNumber
{
get
{
return this.cellNumber;
}
set
{
if (value == this.cellNumber)
{
return;
}
this.cellNumber = value;
this.OnPropertyChanged("CellNumber");
}
}
public SolidColorBrush Color
{
get
{
return this.color;
}
set
{
if (Equals(value, this.color))
{
return;
}
this.color = value;
this.OnPropertyChanged("Color");
}
}
public bool IsSelected { get; set; }
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Happy coding!
I just can't figure it out. What I am missing to bound the Textblock?
I need the TextBlock to update everytime I select a new item in the ListView.
This is a sample I made. I my real application, I used the id from the ListView1 to fetch something from my DB that I want to display in my textBlock..
I know WPF binds to Properties and I need to implement INotifyPropertyChanged but I can't get the bindings right or maybe I am missing something else?
I have added DateTime.Now.TosString() just to see more clearly if the TextBlock changes.
XAML:
<Window x:Class="WpfSO.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>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" x:Name="txtBlockPerson"
Text="{Binding MyPerson}" />
<ListView Grid.Row="1" Grid.Column="0" x:Name="ListView1"
ItemsSource="{Binding ListData}"
IsSynchronizedWithCurrentItem="True"
SelectionChanged="ListView1_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
C#
using System;
using System.ComponentModel;
using System.Windows;
using System.Collections.ObjectModel;
namespace WpfSO
{
public partial class MainWindow : Window
{
private ObservableCollection<Person> ListData { get; set; }
private const string _myName = "You clicked on: ";
public Person MyPerson { get; set; }
public MainWindow()
{
InitializeComponent();
// TextBlock
MyPerson = new Person(_myName);
txtBlockPerson.DataContext = MyPerson;
// ListView
ListData = new ObservableCollection<Person>();
var p1 = new Person("p1");
var p2 = new Person("p2");
ListData.Add(p1);
ListData.Add(p2);
ListView1.ItemsSource = ListData;
}
private void ListView1_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
MyPerson.Name = _myName + ListView1.SelectedItem + ". Time: " +DateTime.Now.ToString();
}
}
public class Person : INotifyPropertyChanged
{
private string _name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged("PersonName");
}
}
}
public Person(string name)
{
Name = name;
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
The OnPropertyChanged needs to have the correct name of your property..
Instead of OnPropertyChanged("PersonName"); use
OnPropertyChanged("Name");