I am attempting to change a picture when clicking on it in the sidebar menu, but however nothing seem to happen when I click on a image. This is done in WPF and for a image editing program. I have been reading up on custom commands and reading other custom commands posts here on StackOverflow but I can't seem to find anything that apply to this case.
public partial class ImageEditor : UserControl
{
ICommand changePicture;
public ICommand ChangePicture
{
get { return changePicture; }
set { changePicture = value; }
}
public ImageEditor()
{
InitializeComponent();
}
public void loadImages(string _imageFile)
{
string directory = Path.GetDirectoryName(_imageFile);
string[] _filePaths = Directory.GetFiles(directory, "*.jpg");
foreach (string image in _filePaths)
{
if (image.Length > 0)
{
thumbNails.Children.Add(addPictureButton(image));
}
}
}
public Button addPictureButton(string image)
{
Image currentImage = new Image();
currentImage.Source = new BitmapImage(new Uri(image));
currentImage.Stretch = Stretch.Fill;
Grid grid = new Grid();
grid.Height = 90;
grid.Children.Add(currentImage);
var newButton = new MyButton();
newButton.Content = grid;
newButton.Name = "button";
newButton.Height = 100;
newButton.Background = Brushes.Chocolate;
newButton.ImageRef = image;
newButton.Command = ChangePicture;
return newButton;
}
}
I think there is a problem with my custom command, this is the main method where I load all the pictures.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
/// Get the Handle for the Forms System Menu
IntPtr systemMenuHandle = GetSystemMenu(this.Handle, false);
/// Create our new System Menu items just before the Close menu item
InsertMenu(systemMenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); // <-- Add a menu seperator
InsertMenu(systemMenuHandle, 6, MF_BYPOSITION, _SettingsSysMenuID, "Inställningar...");
InsertMenu(systemMenuHandle, 7, MF_BYPOSITION, _CalibrateSysMenuID, "Kalibrerings Läge (På/Av)");
InsertMenu(systemMenuHandle, 8, MF_BYPOSITION, _ZoomPrevSysMenuID, "Zoom Läge -1");
InsertMenu(systemMenuHandle, 9, MF_BYPOSITION, _ZoomNextSysMenuID, "Zoom Läge +1");
//InsertMenu(systemMenuHandle, 10, MF_BYPOSITION, _PrintSysMenuID, "Print...");
// Attach our WndProc handler to this Window
HwndSource source = HwndSource.FromHwnd(this.Handle);
source.AddHook(new HwndSourceHook(WndProc));
if (ImageFile != null && ImageFile.Length > 0)
{
if (_cameraManager != null)
_cameraManager.IsPaused = true;
Bitmap bitmap = new Bitmap(ImageFile);
UcImageGrabbedControls.SetImageToView(bitmap);
ImagePicker.loadImages(ImageFile);
ImagePicker.ChangePicture = new CustomCommand(OnPictureClick);
BtnGrab.IsToggled = true;
UpdateShownControls();
}
}
Button goes here
public void OnPictureClick(object sender)
{
var button = (UserControls.MyButton)sender;
if (button == null)
return;
var bitmap = new Bitmap(button.ImageRef);
UcImageGrabbedControls.SetImageToView(bitmap);
}
Custom command implementation
public class CustomCommand : ICommand
{
//public delegate void ExecutedMethod();
//private ExecutedMethod method;
private Action<object> onPictureClick;
public CustomCommand(/*ExecutedMethod executed*/)
{
//method = executed;
}
public CustomCommand(Action<object> onPictureClick)
{
this.onPictureClick = onPictureClick;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
//method();
}
}
This class looks like a lot of people tinkered with it until it broke, so I removed everything that is no longer in use anyway.
public class CustomCommand : ICommand
{
private Action<object> onPictureClick;
public CustomCommand(Action<object> onPictureClick)
{
this.onPictureClick = onPictureClick;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
// Nothing happening here :(
}
}
The basic problem is, that the Execute method doesn't do anything - unsurprisingly any button executing this command doesn't do anything either. The simplest fix would be to simply restore functionality to this method by invoking the method passed to the constructor:
public void Execute(object parameter)
{
onPictureClick.Invoke(parameter);
// or onPictureClick(parameter)
}
Since you are already working on that method, I suggest you rename the action to something less specific, e.g _action.
If you ever intend (or need) to make use of the CanExecute feature and/or use the CommandParameter I suggest you take a look at a RelayCommand implementation.
(This answer has a detailed explanation).
Related
I created an ObservableCollectionEx.cs class that inherits the ObservableCollection class to suppress notifications while the collection is being updated until it's done updating from the answer here.
The class:
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
private bool _notificationSupressed = false;
private bool _supressNotification = false;
public bool SupressNotification
{
get
{
return _supressNotification;
}
set
{
_supressNotification = value;
if (_supressNotification == false && _notificationSupressed)
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
_notificationSupressed = false;
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (SupressNotification)
{
_notificationSupressed = true;
return;
}
base.OnCollectionChanged(e);
}
}
A collection of models is created in a class that is meant to update in response to a number of events. One is based on an observable sequence that simply updates the collection at an interval and another is based on a button click event. Stepping through the codes, I see that both events are causing the collection to update successfully, but only the button click causes the WPF ListView to be notified and updated accordingly. The UI is a WPF UserControl that is used to create a CustomTaskPane in Microsoft Word using VSTO.
The code that updates the collection via Observable sequence:
public partial class CrossReferenceControl : UserControl, ICrossReferenceControl
{
private ICrossReferenceControlViewModel referenceControlViewModel;
private IOpenDocumentModel OpenDocumentModel;
private ICrossReferenceGuy CrossReferenceGuy;
private bool isOpen;
private IObservable<bool> openDocModelUpdateObservable;
private static TimeSpan period = TimeSpan.FromSeconds(20);
private IObservable<long> observable = Observable.Interval(period);
public readonly Subject<bool> OpenDocModelUpdateActionSubject = new Subject<bool>();
public ICrossReferenceControlViewModel ReferenceControlViewModel => referenceControlViewModel;
public bool IsOpen
{
get { return isOpen; }
set { isOpen = value; }
}
public CrossReferenceControl(IOpenDocumentModel openDocumentModel, ICrossReferenceControlViewModel referenceControlViewModel, ICrossReferenceGuy crossReferenceGuy)
{
InitializeComponent();
this.referenceControlViewModel = referenceControlViewModel;
OpenDocumentModel = openDocumentModel;
CrossReferenceGuy = crossReferenceGuy;
//CrossReferenceControlViewModel controlViewModel = new CrossReferenceControlViewModel((OpenDocumentModel)openDocumentModel);
DataContext = referenceControlViewModel;
observable.Subscribe(O => OpenDocumentModel.UpdateCaptionsSubject.OnNext(IsOpen));
}
}
The code that updates via button click event (this works fine):
private void ButtonRefresh_Click(object sender, RoutedEventArgs e)
{
OpenDocumentModel.UpdateCaptionsSubject.OnNext(IsOpen);
}
Note: The codes are cut down to provide only what I think is essential.
I have many controls in a window. Requirement is to know which control gets the focus from the lost focus event of a control.
Say, A Text box and it has the focus. Now I am clicking a button. while doing this, need to know that i am moving the focus to button from the Text box lost focus event.
So how could i achieve this..
This is what I did and its working for me
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
lostFocusControl = e.OldFocus;
}
private void PauseBttn_PreviewKeyDown(object sender, KeyEventArgs e)
{
/**invoke OnPreviewLostKeyboardFocus handller**/
}
Hope it will help
You can use FocusManager to handle this,
In your LostFocusEvent, Use FocusManager.GetFocusedElement()
uiElement.LostFocus+=(o,e)=>
{
var foo=FocusManager.GetFocusedElement();
}
The following class watches the FocusManager for changes in focus, it's a looped thread so you have to put up with the fact that it's running but when focus changes it will just raise an event letting you know what changed.
Just add these two classes to your project.
public class FocusNotifierEventArgs : EventArgs
{
public object OldObject { get; set; }
public object NewObject { get; set; }
}
public class FocusNotifier : IDisposable
{
public event EventHandler<FocusNotifierEventArgs> OnFocusChanged;
bool isDisposed;
Thread focusWatcher;
Dispatcher dispatcher;
DependencyObject inputScope;
int tickInterval;
public FocusNotifier(DependencyObject inputScope, int tickInterval = 10)
{
this.dispatcher = inputScope.Dispatcher;
this.inputScope = inputScope;
this.tickInterval = tickInterval;
focusWatcher = new Thread(new ThreadStart(FocusWatcherLoop))
{
Priority = ThreadPriority.BelowNormal,
Name = "FocusWatcher"
};
focusWatcher.Start();
}
IInputElement getCurrentFocus()
{
IInputElement results = null;
Monitor.Enter(focusWatcher);
dispatcher.BeginInvoke(new Action(() =>
{
Monitor.Enter(focusWatcher);
results = FocusManager.GetFocusedElement(inputScope);
Monitor.Pulse(focusWatcher);
Monitor.Exit(focusWatcher);
}));
Monitor.Wait(focusWatcher);
Monitor.Exit(focusWatcher);
return results;
}
void FocusWatcherLoop()
{
object oldObject = null;
while (!isDisposed)
{
var currentFocus = getCurrentFocus();
if (currentFocus != null)
{
if (OnFocusChanged != null)
dispatcher.BeginInvoke(OnFocusChanged, new object[]{ this, new FocusNotifierEventArgs()
{
OldObject = oldObject,
NewObject = currentFocus
}});
oldObject = currentFocus;
}
}
Thread.Sleep(tickInterval);
}
}
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
}
}
}
Then in your code behind, create a new instance of the Focus Notifier class and hook on to it's OnFocusChanged event, remember to dispose it at the end or the thread will keep your app open.
public partial class MainWindow : Window
{
FocusNotifier focusNotifier;
public MainWindow()
{
InitializeComponent();
focusNotifier = new FocusNotifier(this);
focusNotifier.OnFocusChanged += focusNotifier_OnFocusChanged;
}
void focusNotifier_OnFocusChanged(object sender, FocusNotifierEventArgs e)
{
System.Diagnostics.Debug.WriteLine(e.OldObject);
System.Diagnostics.Debug.WriteLine(e.NewObject);
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
focusNotifier.Dispose();
base.OnClosing(e);
}
}
have you tried to register your controls to Control.LostFocus event and there you can check for Form.ActiveControl, to determine which control currently has the focus
I have a panel with a button on it that is used to trigger an image capture from an external camera. The capture can take several seconds, so I want the button to disable when capture is in progress. I also want to be able to prevent the user capturing when my program is running a control script. Here is my ViewModel class:
public class CameraControlViewModel : ViewModelBase
{
public CameraControlViewModel()
{
}
public CameraControlViewModel( DataModel dataModel )
: base( dataModel )
{
dataModel.PropertyChanged += DataModelOnPropertyChanged;
_captureImageCommand = new RelayCommand( captureImage );
_capturedImage = new BitmapImage();
_capturedImage.BeginInit();
_capturedImage.UriSource = new Uri( "Images/fingerprint.jpg", UriKind.Relative );
_capturedImage.CacheOption = BitmapCacheOption.OnLoad;
_capturedImage.EndInit();
}
public ICommand CaptureImageCommand
{
get { return _captureImageCommand; }
}
public bool CanCaptureImage
{
get { return !dataModel.IsScriptRunning && !_captureInProgress; }
}
public bool IsCaptureInProgress
{
get { return _captureInProgress; }
set
{
if (_captureInProgress != value)
{
_captureInProgress = value;
OnPropertyChanged( "IsCaptureInProgress" );
OnPropertyChanged( "CanCaptureImage" );
}
}
}
public int PercentDone
{
get { return _percentDone; }
set
{
if (_percentDone != value)
{
_percentDone = value;
OnPropertyChanged( "PercentDone" );
}
}
}
public BitmapImage CapturedImage
{
get { return _capturedImage; }
}
private void DataModelOnPropertyChanged( object sender, PropertyChangedEventArgs propertyChangedEventArgs )
{
string property = propertyChangedEventArgs.PropertyName;
if (property == "IsScriptRunning")
{
OnPropertyChanged( "CanCaptureImage" );
}
OnPropertyChanged( property );
}
private void captureImage( object arg )
{
IsCaptureInProgress = true;
PercentDone = 0;
// TODO: remove this placeholder.
new FakeImageCapture( this );
// TODO (!)
}
internal void captureComplete()
{
IsCaptureInProgress = false;
}
// Remove this placeholder when we can take images.
private class FakeImageCapture
{
CameraControlViewModel _viewModel;
int _count;
Timer _timer = new Timer();
public FakeImageCapture( CameraControlViewModel viewModel )
{
this._viewModel = viewModel;
_timer.Interval = 50;
_timer.Elapsed += TimerOnTick;
_timer.Start();
}
private void TimerOnTick( object sender, EventArgs eventArgs )
{
++_count;
if (_count <= 100)
{
_viewModel.PercentDone = _count;
}
else
{
Application.Current.Dispatcher.Invoke( (Action)_viewModel.captureComplete );
_timer.Stop();
_timer = null;
_viewModel = null;
}
}
}
private readonly ICommand _captureImageCommand;
private volatile bool _captureInProgress;
private BitmapImage _capturedImage;
private int _percentDone;
}
Here is the XAML for the button:
<Button Command="{Binding CaptureImageCommand}"
Grid.Row="0" Grid.Column="0"
Margin="4"
IsEnabled="{Binding CanCaptureImage}"
ToolTip="Capture Image">
<Image Source="../Images/camera-icon.gif" Width="64" Height="64" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Button>
Clicking the "capture" button goes fine. The button disables and elsewhere a progress bar appears showing the (currently faked) image capture progress. However, when the capture completes, even though I set the CanCaptureImage property in the captureComplete() method, the button does not change back to its "enabled" appearance. It will only do this when I click somewhere (anywhere) in the window. However, the button is actually enabled because I can click on it again to trigger a 2nd capture.
I have tried CommandManager.InvalidateRequerySuggested() inside captureComplete() but that doesn't help. Any ideas?
Rather than having a separate IsEnabled binding to enable/disable the button, you should really just use the CanExecute predicate of the RelayCommand: http://msdn.microsoft.com/en-us/library/hh727783.aspx
This would ensure that the button will get enabled/disabled properly when calling CommandManager.InvalidateRequerySuggested(). Get rid of the CanCaptureImage property and modify your code as follows:
public CameraControlViewModel( DataModel dataModel )
: base( dataModel )
{
dataModel.PropertyChanged += DataModelOnPropertyChanged;
_captureImageCommand = new RelayCommand( captureImage, captureImage_CanExecute );
_capturedImage = new BitmapImage();
_capturedImage.BeginInit();
_capturedImage.UriSource = new Uri( "Images/fingerprint.jpg", UriKind.Relative );
_capturedImage.CacheOption = BitmapCacheOption.OnLoad;
_capturedImage.EndInit();
}
private bool captureImage_CanExecute( object arg)
{
return !dataModel.IsScriptRunning && !_captureInProgress;
}
I've been using WPF for a while but I'm new to Commands, but would like to start using them properly for once. Following a code example, I've established a separate static Commands class to hold all of my commands, and it looks like this.
public static class Commands
{
public static RoutedUICommand OpenDocument { get; set; }
static Commands()
{
OpenDocument = new RoutedUICommand("Open Document", "OpenDocument", typeof(Commands));
}
public static void BindCommands(Window window)
{
window.CommandBindings.Add(new CommandBinding(OpenDocument, OpenDocument_Executed, OpenDocument_CanExecute));
}
private static void OpenDocument_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// Should be set to true if an item is selected in the datagrid.
}
private static void OpenDocument_Executed(object sender, ExecutedRoutedEventArgs e)
{
}
}
My problem is that although the command is going to be bound to a Button control in MainWindow.xaml, the OpenDocument_CanExecute method needs to look at a DataGrid in MainWindow.xaml to see if an item is selected.
How can I wire things up such that the method can see the DataGrid?
SOLUTION
Inspired by Ken's reply (thanks again!), I put the following in place, which works perfectly.
MainWindow.xaml.cs
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Loaded += delegate
{
DataContext = ViewModel.Current;
Commands.BindCommands(this);
};
}
}
ViewModel.cs
public class ViewModel
{
private static ViewModel _current;
public static ViewModel Current
{
get { return _current ?? (_current = new ViewModel()); }
set { _current = value; }
}
public object SelectedItem { get; set; }
}
Commands.cs
public static class Commands
{
public static RoutedUICommand OpenDocument { get; set; }
static Commands()
{
OpenDocument = new RoutedUICommand("Open Document", "OpenDocument", typeof(Commands));
}
public static void BindCommands(Window window)
{
window.CommandBindings.Add(new CommandBinding(OpenDocument, OpenDocument_Executed, OpenDocument_CanExecute));
}
private static void OpenDocument_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ViewModel.Current.SelectedItem != null;
}
private static void OpenDocument_Executed(object sender, ExecutedRoutedEventArgs e)
{
}
}
ICommand implementations work best in the MVVM pattern:
class ViewModel : INotifyPropertyChanged {
class OpenDocumentCommand : ICommand {
public bool CanExecute(object parameter) {
return ViewModel.ItemIsSelected;
}
public OpenDocumentCommand(ViewModel viewModel) {
viewModel.PropertyChanged += (s, e) => {
if ("ItemIsSelected" == e.PropertyName) {
RaiseCanExecuteChanged();
}
};
}
}
private bool _ItemIsSelected;
public bool ItemIsSelected {
get { return _ItemIsSelected; }
set {
if (value == _ItemIsSelected) return;
_ItemIsSelected = value;
RaisePropertyChanged("ItemIsSelected");
}
}
public ICommand OpenDocument {
get { return new OpenDocumentCommand(this); }
}
}
Obviously, I left out a whole bunch of stuff. But this pattern has worked well for me in the past.
why even implement a command if you are tightly coupling it to UI implementation? Just respond to datagrid.SelectionChanged and code in what supposed to happen.
Otherwise, put it in the ViewModel. Have the ViewModel monitor it's state and evaluate when CanExe is true.
Edit
On the other hand, you can pass a parameter to your command, as well as Exe() & CanExe() methods
//where T is the type you want to operate on
public static RoutedUICommand<T> OpenDocument { get; set; }
If you are doing an MVVM solution, this would be the perfect time to implement a publish / subscribe aggregator that allows controls to "talk" to each other. The gist behind it is that the datagrid would publish an event, 'Open Document'. Subsequent controls could subscribe to the event and react to the call to 'Open Document'. The publish / subscribe pattern prevents tightly coupling the datagrid and the control. Do some searches for event aggregators and I think you'll be on your way.
I have a list of classes, but different children have different properties that need to be displayed.
What I want to achieve is to have a listbox-type control in the gui which enables each child to display it's properties the way it wants to - so not using the same pre-defined columns for every class.
I envisage something like the transmission interface (below), where each class can paint it's own entry, showing some text, progress bar if relevant, etc.
How can this be achieved in C#?
Thanks for any help.
Let your list items implement an interface that provides everything needed for the display:
public interface IDisplayItem
{
event System.ComponentModel.ProgressChangedEventHandler ProgressChanged;
string Subject { get; }
string Description { get; }
// Provide everything you need for the display here
}
The transmission objects should not display themselves. You should not mix domain logic (business logic) and display logic.
Customized ListBox:
In order to do display listbox items your own way, you will have to derive your own listbox control from System.Windows.Forms.ListBox. Set the DrawMode property of your listbox to DrawMode.OwnerDrawFixed or DrawMode.OwnerDrawVariable (if the items are not of the same size) in the constructor. If you use OwnerDrawVariable then you will have to override OnMeasureItem as well, in order to tell the listbox the size of each item.
public class TransmissionListBox : ListBox
{
public TransmissionListBox()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index >= 0 && e.Index < Items.Count) {
var displayItem = Items[e.Index] as IDisplayItem;
TextRenderer.DrawText(e.Graphics, displayItem.Subject, e.Font, ...);
e.Graphics.DrawIcon(...);
// and so on
}
e.DrawFocusRectangle();
}
}
You can let your original transmission class implement IDisplayItem or create a special class for this purpose. You can also have different types of objects in the list, as long as they implement the interface. The point is, that the display logic itself is in the control, the transmission class (or whatever class) only provides the information required.
Example:
Because of the ongoing discussion with Mark, I have decided to include a full example here. Let's define a model class:
public class Address : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name != value) {
_Name = value;
OnPropertyChanged("Name");
}
}
}
private string _City;
public string City
{
get { return _City; }
set
{
if (_City != value) {
_City = value;
OnPropertyChanged("City");
OnPropertyChanged("CityZip");
}
}
}
private int? _Zip;
public int? Zip
{
get { return _Zip; }
set
{
if (_Zip != value) {
_Zip = value;
OnPropertyChanged("Zip");
OnPropertyChanged("CityZip");
}
}
}
public string CityZip { get { return Zip.ToString() + " " + City; } }
public override string ToString()
{
return Name + "," + CityZip;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Here is a custom ListBox:
public class AddressListBox : ListBox
{
public AddressListBox()
{
DrawMode = DrawMode.OwnerDrawFixed;
ItemHeight = 18;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
const TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter;
if (e.Index >= 0) {
e.DrawBackground();
e.Graphics.DrawRectangle(Pens.Red, 2, e.Bounds.Y + 2, 14, 14); // Simulate an icon.
var textRect = e.Bounds;
textRect.X += 20;
textRect.Width -= 20;
string itemText = DesignMode ? "AddressListBox" : Items[e.Index].ToString();
TextRenderer.DrawText(e.Graphics, itemText, e.Font, textRect, e.ForeColor, flags);
e.DrawFocusRectangle();
}
}
}
On a form, we place this AddressListBox and a button. In the form, we place some initializing code and some button code, which changes our addresses. We do this in order to see, if our listbox is updated automatically:
public partial class frmAddress : Form
{
BindingList<Address> _addressBindingList;
public frmAddress()
{
InitializeComponent();
_addressBindingList = new BindingList<Address>();
_addressBindingList.Add(new Address { Name = "Müller" });
_addressBindingList.Add(new Address { Name = "Aebi" });
lstAddress.DataSource = _addressBindingList;
}
private void btnChangeCity_Click(object sender, EventArgs e)
{
_addressBindingList[0].City = "Zürich";
_addressBindingList[1].City = "Burgdorf";
}
}
When the button is clicked, the items in the AddressListBox are updated automatically. Note that only the DataSource of the listbox is defined. The DataMember and ValueMember remain empty.
yes, if you use WPF it is quite easy to do this. All you have to do is make a different DataTemplate for your different types.
MSDN for data templates
Dr. WPF for Items Control & Data Templates