Blackmagic decklink CapturePreviewCSharp fails to capture video - c#

I have run the project CapturePreviewCSharp provided as a sample. However, it seems to be able to acquire the device status, etc., but the image is not output to the preview screen.
There are no errors, etc.
It seems that the rendering to D3DImage is not working, but I don't know how to solve this problem.
Does anyone else have a similar problem?
I have asked on the Blackmagic forum but have not gotten an answer.
Development environment.
Windows 11.
Visual Studio 2022
NET framework 4.8
DeckLink mini-recorder 4K
Here is the code on the Xaml side
<Window x:Class="CapturePreviewCSharp.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:i="clr-namespace:System.Windows.Interop;assembly=PresentationCore"
<!--excerpt code-->
<GroupBox x:Name="groupBoxPreview" Header="Preview" >
<Grid x:Name="gridPreview" Margin="5">
<Image x:Name="imagePreview">
<Image.Source>
<i:D3DImage x:Name="d3dPreview" />
</Image.Source>
</Image>
</Grid>
</GroupBox>
</DockPanel>
</Window>
Code to output images to D3DImage.
public void RenderD3DImage(object sender, EventArgs e)
{
UpdateUIElement(d3dPreview, new Action(() =>
{
var actualWidth = gridPreview.RenderSize.Width;
var actualHeight = gridPreview.RenderSize.Height;
if (d3dPreview.IsFrontBufferAvailable)
{
IntPtr surface = IntPtr.Zero;
if (actualWidth > 0 && actualHeight > 0)
{
new MTAAction(() =>
{
m_previewCallback.PreviewHelper.SetSurfaceSize((uint)actualWidth, (uint)actualHeight);
m_previewCallback.PreviewHelper.GetBackBuffer(out surface);
});
}
if (surface != IntPtr.Zero)
{
d3dPreview.Lock();
d3dPreview.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface);
new MTAAction(() => m_previewCallback.PreviewHelper.Render());
d3dPreview.AddDirtyRect(new Int32Rect(0, 0, d3dPreview.PixelWidth, d3dPreview.PixelHeight));
d3dPreview.Unlock();
}
}
}));
}
MTAAction class.
namespace CapturePreviewCSharp
{
class MTAAction
{
private ManualResetEvent doneEvent;
private readonly Action action;
public MTAAction(Action action)
{
this.action = action;
doneEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(MTAActionThreadPoolCallback, 0);
doneEvent.WaitOne();
}
public void MTAActionThreadPoolCallback(Object threadContext)
{
action();
doneEvent.Set();
}
}
class MTAFunc<T> where T : struct
{
private T value;
private readonly Func<T> getValueFunc;
private ManualResetEvent doneEvent;
public MTAFunc(Func<T> func)
{
value = default(T);
getValueFunc = func;
doneEvent = new ManualResetEvent(false);
}
public T Value
{
get
{
ThreadPool.QueueUserWorkItem(MTAFuncThreadPoolCallback, 0);
doneEvent.WaitOne();
return (T)value;
}
}
public void MTAFuncThreadPoolCallback(Object threadContext)
{
value = getValueFunc();
doneEvent.Set();
}
}
}
Thank you.

Related

Call an ICommand from Windows.Forms.NotifyIcon Event .MouseClick() [WPF Application]

I have created a custom NotifyIcon object inherited from Window Forms. Looking for the properties and the methods of the NotifyIcon Class here I saw that I can trigger an Event when the user clicks the NotifyIcon from Taskbar. So I have created the following code inside a ViewModel
namespace TestEnvironment
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public ICommand ShowWindowsCommand //this is the ICommand I want to call in .MouseClick()
{
get { return new DelegateCommand<object>(FunctionShowWindows); }
}
private void FunctionShowWindows(object parameter)
{
ProgressBarTemplate ProgressBarInstance = (ProgressBarTemplate)Application.Current.Windows.OfType<Window>().SingleOrDefault(window => window.Name == "ProgressBarScreen");
if (ProgressBarInstance != null)
{
if (MainWindowInstance.MaxHeight > 725 & MainWindowInstance.MaxWidth > 1200)
{
MainWindowInstance.WindowState = WindowState.Maximized;
MainWindowInstance.Activate();
//MainWindowInstance.Topmost = true;
}
else
{
MainWindowInstance.WindowState = WindowState.Normal;
MainWindowInstance.Activate();
//MainWindowInstance.Topmost = true;
}
ProgressBarInstance.Show();
ProgressBarInstance.WindowState = WindowState.Normal;
//ProgressBarInstance.Topmost = true;
}
else
{
if (MainWindowInstance.MaxHeight > 725 & MainWindowInstance.MaxWidth > 1200)
{
MainWindowInstance.WindowState = WindowState.Maximized;
MainWindowInstance.Activate();
//MainWindowInstance.Topmost = true;
}
else
{
MainWindowInstance.WindowState = WindowState.Normal;
MainWindowInstance.Activate();
//MainWindowInstance.Topmost = true;
}
}
}
public ICommand RunCalculationCommand_Approach2
{
get { return new DelegateCommand<object>(ExecuteSqlAsync); }
}
private async void ExecuteSqlAsync(object obj)
{
Stream iconStream_one = Application.GetResourceStream(new Uri("pack://application:,,,/TestApp;component/Assets/loading.ico")).Stream;
System.Windows.Forms.NotifyIcon notification_object = new System.Windows.Forms.NotifyIcon
{
Icon = new Icon(iconStream_one),
Visible = true
};
// The complete Task API accepts a CancellationToken to allow cancellation.
try
{
DateTime timestamp_start = DateTime.Now;
await Task.Run(() => RunCalculationsMethod(object_progressbar, "LOG_DETAILS", 1, true, getconnectionstring, CatchErrorExceptionMessage, this.CancellationTokenSource.Token), this.CancellationTokenSource.Token);
string[] time_passed = DateTime.Now.Subtract(timestamp_start).ToString().Split(#":");
List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
reportsucessfulcompletion = CheckLogsFailSuccessProcedure(SQLServerConnectionDetails());
if (reportsucessfulcompletion[0].Result == 0)
{
notification_object.ShowBalloonTip(5000, "Hi", "This is a BallonTip from Windows Notification", System.Windows.Forms.ToolTipIcon.None);
//notification_object.MouseClick += new System.EventHandler(ShowWindowsCommand);
}
else
{
notification_object.ShowBalloonTip(5000, "Hi", "Hello World", System.Windows.Forms.ToolTipIcon.None);
//notification_object.MouseClick += new System.EventHandler(ShowWindowsCommand);
}
}
catch (Exception ex)
{
//..
}
finally
{
//..
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
Closing += CancelSqlOperationsOnClosing;
}
}
}
XAML
<Window x:Class="TestEnvironment.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:TestEnvironment"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button Content="Show Toast"
Padding="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding RunCalculationCommand_Approach2}"/>
</Grid>
</Window>
What I want is to call the ICommand ShowWindowsCommand from the Event MouseClick.
//notification_object.MouseClick += new System.EventHandler(ShowWindowsCommand); -> This is what I want to fix
But this Event accepts EventHandlers. So, is there any way to call the ICommand from the MouseClick event of the NotifyIcon?
Since NotifyIcon is not actually a control that displays on a page, you can instantiate it in the ViewModel. In order to trigger the command when clicking on the icon, you simply need to implement a callback to the event.
MainWindowViewModel.cs
namespace TestEnvironment
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public ICommand ShowWindowsCommand
{
get { return new DelegateCommand<object>(FunctionShowWindows); }
}
private void FunctionShowWindows(object parameter)
{
// redacted
}
public ICommand RunCalculationCommand_Approach2
{
get { return new DelegateCommand<object>(ExecuteSqlAsync); }
}
private async void ExecuteSqlAsync(object obj)
{
Stream iconStream_one = Application.GetResourceStream(new Uri("pack://application:,,,/TestApp;component/Assets/loading.ico")).Stream;
System.Windows.Forms.NotifyIcon notification_object = new System.Windows.Forms.NotifyIcon
{
Icon = new Icon(iconStream_one),
Visible = true
};
notification_object.MouseClick += new System.Windows.Forms.MouseEventHandler(NotifyIcon_MouseClick);
// redacted
}
private void NotifyIcon_MouseClick(object sender, EventArgs e);
{
FunctionShowWindows(null);
}
}
}

Display progress during matrix loop?

I'm making a chessboard, I want to see progress while it's done.
Chessboard is not classical, it contains millions of fields, the process of creation itself takes time. Sometimes the creation process takes up to 2 minutes. I want to visually see when the process itself will be over. It does not have to be a progress bar, it can be any control that will not slow down the process itself.
When I use Progress.Dispatcher.Invoke (()... I actually slow down the creation process and it takes 5 times longer than usual. When I use BackgroundWorker and ReportProgress ... I also slow down the creation process and it takes 5 to 8 times more than usual.
I just want to show the user progress by using any control or class that will not slow down the process. Some idea?
Rectangle[,] square = new Rectangle[x, x];
for (int row = 0; row < x; row++)
for (int col = 0; col < x; col++)
{
square[row, col] = new Rectangle()
{
Height = squareSize,
Width = squareSize
};
Grid.SetColumn(square[row, col], col);
Grid.SetRow(square[row, col], row);
if ((row + col) % 2 == 0)
{
square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(233, 223, 191));
}
else
{
square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(112, 42, 44));
}
LayoutRoot.Children.Add(square[row, col]);
// Watch process of creation in real time
if (cbCreationProcess.IsChecked == true)
Progress.Dispatcher.Invoke(() => Progress.Value = x, DispatcherPriority.Background);
}
This solution has worked for me in the past.
ProgressWindowControl.xaml
<Window
x:Class="YourNamespace.ProgressWindowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
WindowStartupLocation="CenterScreen" ShowInTaskbar="False" ResizeMode="NoResize"
SizeToContent="WidthAndHeight" WindowStyle="None"
mc:Ignorable="d">
<Window.Style>
<Style TargetType="Window">
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="Background" Value="#00FFFFFF"/>
</Style>
</Window.Style>
<Grid>
<Grid Width="450" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0">
<Grid x:Name="Back">
<Border Background="Black" CornerRadius="3" Opacity="0.15"/>
<Border CornerRadius="2" Margin="1" Background="White"/>
</Grid>
<Grid x:Name="Content_Area" Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="Info" TextWrapping="Wrap"
Text="{Binding Path=State,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Grid.Row="0" Margin="12,12,12,0" Foreground="#FF2D2D2D"/>
<ProgressBar Height="12"
Grid.Row="1"
Margin="12"
IsIndeterminate="{Binding Path=IsIndeterminate,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Value="{Binding Path=Progress,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Maximum="{Binding Path=MaxProgress,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<Button x:Name="uxCancelBtn" Grid.Row="2" Height="22" Width="85" HorizontalAlignment="Right" Margin="0 0 12 0"
Click="CancelButton_Click" IsEnabled="False" Content="{x:Static resx:Strings.Cancel}">
</Button>
</Grid>
</Grid>
</Grid>
</Window>
ProgressWindowControl.cs
public sealed partial class ProgressWindowControl : Window
{
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register("Progress", typeof(double), typeof(ProgressWindowControl), new PropertyMetadata(0d));
public static readonly DependencyProperty MaxProgressProperty =
DependencyProperty.Register("MaxProgress", typeof(double), typeof(ProgressWindowControl), new PropertyMetadata(100d));
public static readonly DependencyProperty IsIndeterminateProperty =
DependencyProperty.Register("IsIndeterminate", typeof(bool), typeof(ProgressWindowControl), new PropertyMetadata(true));
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(string), typeof(ProgressWindowControl), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IsCancelAllowedProperty =
DependencyProperty.Register("IsCancelAllowed", typeof(bool), typeof(ProgressWindowControl), new PropertyMetadata(false));
private ProgressWindowControl()
{
InitializeComponent();
}
public double Progress
{
get
{
return (double)GetValue(ProgressProperty);
}
set
{
SetValue(ProgressProperty, value);
}
}
public double MaxProgress
{
get
{
return (double)GetValue(MaxProgressProperty);
}
set
{
SetValue(MaxProgressProperty, value);
}
}
public bool IsIndeterminate
{
get
{
return (bool)GetValue(IsIndeterminateProperty);
}
set
{
SetValue(IsIndeterminateProperty, value);
}
}
public string State
{
get
{
return (string)GetValue(StateProperty);
}
set
{
SetValue(StateProperty, value);
}
}
public Action OnProgressWindowCancel { get; set; }
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (OnProgressWindowCancel != null)
{
uxCancelBtn.IsEnabled = false;
uxCancelBtn.Content = Strings.Cancelling;
OnProgressWindowCancel();
}
}
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
private const int GWL_HWNDPARENT = -8;
private static ProgressWindowControl _progressWindowControl;
private static bool _isVisible;
private static Window _owner;
private static ResizeMode? _ownerResizeMode;
private static bool _ownerIsHitTestVisible;
private static bool _ownerFocusable;
public static void ShowProgressWindow(Window owner = null)
{
if (!_isVisible)
{
IntPtr ownerHandle = IntPtr.Zero;
if (owner != null)
{
_owner = owner;
ownerHandle = GetHandler(_owner);
//Block owner window input while the progress bar is opened
_ownerResizeMode = _owner.ResizeMode;
_ownerIsHitTestVisible = _owner.IsHitTestVisible;
_ownerFocusable = _owner.Focusable;
_owner.ResizeMode = ResizeMode.NoResize;
_owner.IsHitTestVisible = false;
_owner.Focusable = false;
_owner.PreviewKeyDown += Owner_PreviewKeyDown;
_owner.PreviewMouseDown += Owner_PreviewMouseDown;
_owner.Closing += Owner_Closing;
}
//Run window in its own thread
Thread thread = new Thread(new ThreadStart(() =>
{
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
_progressWindowControl = new ProgressWindowControl();
// Shutdown the dispatcher when the window closes
_progressWindowControl.Closed += (s, e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
// When the progress window has loaded, if an owner has been specified, attach it to the window, otherwise set Topmost = true
ProgressWindowControl._progressWindowControl.Loaded += (s, e) =>
{
if (owner != null)
{
IntPtr ownedWindowHandle = GetHandler(_progressWindowControl);
SetOwnerWindowMultithread(ownedWindowHandle, ownerHandle);
}
else
{
_progressWindowControl.Topmost = true;
}
};
_progressWindowControl.Show();
_isVisible = true;
System.Windows.Threading.Dispatcher.Run();
}));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
}
}
private static void Owner_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
}
private static void Owner_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
private static void Owner_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
e.Handled = true;
}
private static void SetOwnerWindowMultithread(IntPtr windowHandleOwned, IntPtr intPtrOwner)
{
if (windowHandleOwned != IntPtr.Zero && intPtrOwner != IntPtr.Zero)
{
SetWindowLong(windowHandleOwned, GWL_HWNDPARENT, intPtrOwner.ToInt32());
}
}
private static IntPtr GetHandler(Window window)
{
var interop = new WindowInteropHelper(window);
return interop.Handle;
}
public static void CloseProgressWindow()
{
if (_progressWindowControl != null && _isVisible)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.Close();
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(_progressWindowControl.Close));
}
if (_owner != null)
{
//Unblock owner input
_owner.ResizeMode = _ownerResizeMode ?? ResizeMode.CanResize;
_owner.IsHitTestVisible = _ownerIsHitTestVisible;
_owner.Focusable = _ownerFocusable;
_owner.PreviewKeyDown -= Owner_PreviewKeyDown;
_owner.PreviewMouseDown -= Owner_PreviewMouseDown;
_owner.Closing -= Owner_Closing;
}
//Reset fields
_ownerResizeMode = null;
_ownerIsHitTestVisible = false;
_ownerFocusable = false;
_progressWindowControl = null;
_owner = null;
_isVisible = false;
}
}
public static void SetProgress(double progress, double maxProgress)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.IsIndeterminate = false;
_progressWindowControl.Progress = progress;
_progressWindowControl.MaxProgress = maxProgress;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.IsIndeterminate = false;
_progressWindowControl.Progress = progress;
_progressWindowControl.MaxProgress = maxProgress;
}));
}
}
}
public static void SetIsIndeterminate(bool isIndeterminate)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.IsIndeterminate = isIndeterminate;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.IsIndeterminate = isIndeterminate;
}));
}
}
}
public static void SetState(string state)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.State = state;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.State = state;
}));
}
}
}
public static void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
{
if (_progressWindowControl != null)
{
if (_progressWindowControl.Dispatcher.CheckAccess())
{
_progressWindowControl.OnProgressWindowCancel = progressWindowCancel;
_progressWindowControl.uxCancelBtn.IsEnabled = isCancelAllowed;
_progressWindowControl.uxCancelBtn.Content = Strings.Cancel;
}
else
{
_progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
_progressWindowControl.OnProgressWindowCancel = progressWindowCancel;
_progressWindowControl.uxCancelBtn.IsEnabled = isCancelAllowed;
_progressWindowControl.uxCancelBtn.Content = Strings.Cancel;
}));
}
}
}
}
A helper class to open the window:
public static class ProgressWindowHelper
{
public static void Show(Window owner = null)
{
ProgressWindowControl.ShowProgressWindow(owner);
}
public static void Close()
{
ProgressWindowControl.CloseProgressWindow();
}
public static void SetProgress(double progress, double maxProgress)
{
ProgressWindowControl.SetProgress(progress, maxProgress);
}
public static void SetIsIndeterminate(bool isIndeterminate)
{
ProgressWindowControl.SetIsIndeterminate(isIndeterminate);
}
public static void SetState(string state)
{
ProgressWindowControl.SetState(state);
}
public static void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
{
ProgressWindowControl.SetIsCancelAllowed(isCancelAllowed, progressWindowCancel);
}
}
A service so you can use Dependency Injection (I didn't include the interface, just create one as needed):
public class ProgressWindowService : IProgressWindowService
{
public void Show(Window owner = null)
{
ProgressWindowHelper.Show(owner);
}
public void Close()
{
ProgressWindowHelper.Close();
}
public void SetProgress(double progress, double maxProgress)
{
ProgressWindowHelper.SetProgress(progress, maxProgress);
}
public void SetIsIndeterminate(bool isIndeterminate)
{
ProgressWindowHelper.SetIsIndeterminate(isIndeterminate);
}
public void SetState(string state)
{
ProgressWindowHelper.SetState(state);
}
public void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
{
ProgressWindowHelper.SetIsCancelAllowed(isCancelAllowed, progressWindowCancel);
}
}
And finally in your ViewModel (assuming you have injected the service):
ProgressBarService.SetProgress(current, total);
ProgressBarService.SetState(state);
ProgressBarService.Show();
You can also pass a window to the Show method and then the window will be attached to it and input will be blocked while the progress window is shown, if no window is provided, the progress bar is shown on top of any other window (TopMost=true):
ProgressBarService.Show(YourWindow);
You could also use a messenger to trigger the progress window.
EDIT
Removed DevExpress dependency.
obviously any addition of functionality is going to slow it down as you're well aware.
what I would do if I was told to use the above code is likely create a timer and poke into where-ever you have your current row and col increments and its up to you what you do with your calculation.
All you would have to do is handle the Timer.Elapsed event (whatever kind of timer) and calculate/report current progress in percent or such.
Otherwise, you could write up a separate thread and run it in a loop that sleeps N-milloseconds until drawing is complete—essentially tandem with the idea of using a timer. Generally, this is what we would do to report progress on a playing mp3 or some such object manipulated on a separate thread.
there is a lot of room for you to be creative here and milk some wisdom out of this scenario to optimize results but before considering such...
bare in mind that WPF is a beast when it comes to how it deals with drawing things since it relies heavily on the video hardware, memory, etc... I tend to think of WPF as like html for OpenGL or DirectX. I hope your GPU has a good connection to the heat-sync and doesn't get too hot and/or that you're not working on a laptop with an embedded GPU. I'm being aggressive here, but only because I've blown pleanty of hardware over the years writing software in the write/compile/run/and-repeat cycle. I'd have gotten much more life out of lots of hardware if I played a bit safer. not to mention recent years are getting harder and harder on our hardware.
for the sake of being as creative as possible here
since we know how many squares there are and can be sure of the resulting grid's dimensions, have you thought rendering a custom pattern as a background and explicitly setting the size of the grid? Once that's done, you could be creative when it comes to further populating needed or spin up some interesting transitions, etc
learning/using System.Threading.Thread(ThreadStart) will enable you to set process-priority to a higher value than standard defaults — as opposed to using BackgroundWorker or some other such... but I haven't quite wrapped my mind around an implementation with such as this.
build up some objects outside the UI such as a List<> and render at the end of a row, column or perhaps every N incremenet
we know that we have x*x boxes in the end, so if we're explicit about the size, there is so much room for creativity here. I hadn't tinkered with WPF in a while, but this would be a good case for studying and looking at different ways to lay such a thing out (eg: CustomLayoutPanel tut). If you're stuck on the notion of a Grid, there is only so much you can do---I would instinctively be working with Drawing methods for doing this, and would likely come up with a way to draw only what is visible on the screen as needed... or
generally #dymanoid's response is poking you into the correct direction but it breaks the logic of the question being asked, therefore overshoots the scope of the question... but we want to enlighten here and generally yes any rendering process stuff in WPF should be thought out using existing optimization strategies such as MVVM and styles/templates/transitions.

Setting `VirtualizingPanel.IsVirtualizing` with TestStack.White

When testing virtualized panels I need to set the VirtualizingPanel.IsVirtualizing Property so that Teststack.White can interact with them like with non virtualized panels.
This helps me especially when panels have a lot of content.
I do not want to set VirtualizingPanel.IsVirtualizing statically so I do not have to deliver it like that to my customers.
To play around with a minimal example you will need a window.
<Window x:Class="DataGridTest.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:DataGridTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<DataGrid
AutomationProperties.AutomationId="MyDataGRID"
ItemsSource="{Binding MyItems}"
VirtualizingPanel.IsVirtualizing="True" >
<!-->
"IsVirtualizing Defaults to True."
"Setting this to False makes the test pass but is a poor choice for production code."
"Somehow I need to be able to change this programatically during testing."
</!-->
</DataGrid>
</Window>
Code behind for the window above.
using System.Collections.Generic;
using System.Windows;
namespace DataGridTest
{
public class Item
{
private string str;
public Item(string str) { this.str = str; }
public string Value { get { return str; } }
public int Length { get { return str.Length; } }
public int Hash { get { return str.GetHashCode(); } }
}
public partial class MainWindow : Window
{
List<Item> myitems;
public List<Item> MyItems { get { return myitems; } }
public MainWindow()
{
InitializeComponent();
myitems = new List<Item>();
for (int i = 0; i < 800; ++i)
{
myitems.Add(new Item($"Item {i}"));
}
DataContext = this;
}
}
}
And finally a Testing project:
using NUnit.Framework;
using System.Diagnostics;
using TestStack.White;
using TestStack.White.UIItems;
using TestStack.White.UIItems.WindowItems;
namespace NunitTest
{
[TestFixture]
public class Class1
{
private Application app;
private Window window;
[OneTimeSetUp]
public void OneTimeSetUp()
{
ProcessStartInfo info = new ProcessStartInfo( $"{TestContext.CurrentContext.WorkDirectory}/DataGridTest.exe");
info.WorkingDirectory = TestContext.CurrentContext.WorkDirectory;
app = Application.Launch(info);
window = app.GetWindow("MainWindow");
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
window.Close(); window = null;
app.Close(); app = null;
}
[Test]
public void test()
{
ListView list = window.Get<ListView>("MyDataGRID");
SetIsVirtualizing(list, false);
Assert.AreEqual(800, list.Rows.Count, "This fails for virtualized panels");
SetIsVirtualizing(list, true);
}
private void SetIsVirtualizing(ListView list, bool value)
{
//insert magic - I tried a couple of things but I just can not set this dependency property
}
}
}
Please help be to understand how VirtualizingPanel.IsVirtualizing can be set during testing.
I had some success with adding a collapsed textbox to interact with the datacontext. Although I am very unhappy about that solution it does pass the testing.
Here are modifications to the code that I made:
window
<StackPanel>
<TextBox
AutomationProperties.AutomationId="MyItems_IsVirtualizing_Injector"
Text="{Binding MyItems_IsVirtualizing_Injector}" Visibility="Collapsed"/>
<DataGrid
AutomationProperties.AutomationId="MyDataGRID"
ItemsSource="{Binding MyItems}"
VirtualizingPanel.IsVirtualizing ="{Binding MyItems_IsVirtualizing}"
>
<!-->
"IsVirtualizing Defaults to True."
"Setting this to False makes the test pass but is a poor choice for production code."
"Somehow I need to be able to change this programatically during testing."
</!-->
</DataGrid>
</StackPanel>
code behind
string injector = true.ToString();
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public string MyItems_IsVirtualizing_Injector
{
get { return injector; }
set
{
injector = value;
PropertyChanged(this, new PropertyChangedEventArgs("MyItems_IsVirtualizing_Injector"));
PropertyChanged(this, new PropertyChangedEventArgs("MyItems_IsVirtualizing"));
}
}
public bool MyItems_IsVirtualizing { get { return string.Equals(true.ToString(), MyItems_IsVirtualizing_Injector); } }
testing
private void SetIsVirtualizing(ListView list, bool value)
{
var injector = window.Get<TextBox>("MyItems_IsVirtualizing_Injector");
injector.Text = value.ToString();
}
EDIT: Since my actual usecase is counting the elements I actually settled for another solution that can be called using CountElements(list.AutomationElement)
private static int CountElements(AutomationElement container)
{
var patterns = container.GetSupportedPatterns();
var itemContainer = container.GetCurrentPattern(ItemContainerPattern.Pattern) as ItemContainerPattern;
List<object> elements = new List<object>();
var element = itemContainer.FindItemByProperty(null, null, null);
while (element != null)
{
elements.Add(element);
element = itemContainer.FindItemByProperty(element, null, null);
}
return elements.Count;
}

AdDuplex Ad Control keeps showing despite collapsing it

I’m creating a WinRT (Windows 8.1 and Windows Phone 8.1) app in which I placed an AdDuplex ad control in one of its pages.
The user of the app can choose to remove the ad (with an IAP). When they do, I set the Visibility of the AdDuplex ad control to Collapsed, from the page ViewModel.
This part works fine; however, after some time, while the user is still on the page, AdDuplex ad control suddenly becomes visible again and starts showing ads.
At the beginning, I thought this is the behavior of IAP when using CurrentAppSimulator, although it didn’t make sense to me, since I have nothing in the code that reacts to license changes and hence setting the control back to Visible. Yet, I tested license.IsActive for my " NoAd” product and got true, indicating that the license is valid.
The following is a simplified part of my code:
MyPage.xaml
<ad:AdControl
AdUnitId="{StaticResource AdUnitId}"
AppKey="{StaticResource AdAppKey}"
IsTest="True"
CollapseOnError="True"
Visibility="{Binding IsNoAdPurchased, Converter={StaticResource BooleanToVisibilityInvertedConverter}}"/>
MyPageViewModel.cs
private async void RemoveAd()
{
this.IsNoAdPurchased = await this.storeService.PurchaseProductAsync(Products.NoAd);
}
StoreService.cs
#if DEBUG
using StoreCurrentApp = Windows.ApplicationModel.Store.CurrentAppSimulator;
#else
using StoreCurrentApp = Windows.ApplicationModel.Store.CurrentApp;
#endif
public sealed class StoreService
{
public async Task<bool> PurchaseProductAsync(string productId)
{
try
{
var purchase = await StoreCurrentApp.RequestProductPurchaseAsync(productId);
return purchase.Status == ProductPurchaseStatus.Succeeded || purchase.Status == ProductPurchaseStatus.AlreadyPurchased;
}
catch (Exception)
{
// The purchase did not complete because an error occurred.
return false;
}
}
}
I made a demo followed by yours and you can refer to it.
xaml part:
<Page.Resources>
<local:MyConverter x:Key="myconverter"></local:MyConverter>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Windows81:AdMediatorControl x:Name="AdMediator" HorizontalAlignment="Left" Height="250" Id="AdMediator-Id-FA61D7FD-4F5F-445D-AB97-DB91618DBC70" Margin="557,287,0,0" VerticalAlignment="Top" Width="300" Visibility="{Binding IsVisible,Converter={StaticResource myconverter}}" />
<Button Name="btn1" Content="Remove ad" Click="RemoveAd" Visibility="Visible" />
</Grid>
code behind:
public class Recording : INotifyPropertyChanged
{
private bool isVisible;
public Recording()
{
}
public bool IsVisible
{
get
{
return isVisible;
}
set
{
if (value != isVisible)
{
isVisible = value;
OnPropertyChanged("IsVisible");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private Recording recording;
public MainPage()
{
this.InitializeComponent();
Init();
recording = new Recording();
recording.IsVisible = false;
this.DataContext = recording;
}
private async void Init()
{
StorageFile proxyFile = await Package.Current.InstalledLocation.GetFileAsync("in-app-purchase.xml");
await CurrentAppSimulator.ReloadSimulatorAsync(proxyFile);
}
public async Task<bool> PurchaseProductAsync(string productId)
{
try
{
var purchase = await CurrentAppSimulator.RequestProductPurchaseAsync(productId);
return purchase.Status == ProductPurchaseStatus.Succeeded || purchase.Status == ProductPurchaseStatus.AlreadyPurchased;
}
catch (Exception)
{
// The purchase did not complete because an error occurred.
return false;
}
}
private async void RemoveAd(object sender, RoutedEventArgs e)
{
recording.IsVisible = await this.PurchaseProductAsync("product2");
}
}
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Boolean && (bool)value)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
I have tested it and after buying the product,the ad will no longer show.
Also I would like to advise you to use another method without binding.
xaml part:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Windows81:AdMediatorControl x:Name="AdMediator" HorizontalAlignment="Left" Height="250" Id="AdMediator-Id-FA61D7FD-4F5F-445D-AB97-DB91618DBC70" Margin="557,287,0,0" VerticalAlignment="Top" Width="300" Visibility="Visible" />
<Button Name="btn1" Content="Remove ad" Click="Button_Click" Visibility="Visible" />
</Grid>
code behind:
namespace AdmediatorTest
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
Init();
LicenseInformation licenseInformation = CurrentAppSimulator.LicenseInformation;
if (!licenseInformation.ProductLicenses["product2"].IsActive)
{
btn1.Visibility = Visibility.Visible;
}
else
{
btn1.Visibility = Visibility.Collapsed;
}
}
private async void Init()
{
StorageFile proxyFile = await Package.Current.InstalledLocation.GetFileAsync("in-app-purchase.xml");
await CurrentAppSimulator.ReloadSimulatorAsync(proxyFile);
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
LicenseInformation licenseInformation = CurrentAppSimulator.LicenseInformation;
if (!licenseInformation.ProductLicenses["product2"].IsActive)
{
try
{
await CurrentAppSimulator.RequestProductPurchaseAsync("product2");
if (licenseInformation.ProductLicenses["product2"].IsActive)
{
AdMediator.Visibility = Visibility.Collapsed;
}
else
{
AdMediator.Visibility = Visibility.Visible;
}
}
catch (Exception)
{
//rootPage.NotifyUser("Unable to buy " + productName + ".", NotifyType.ErrorMessage);
}
}
else
{
//rootPage.NotifyUser("You already own " + productName + ".", NotifyType.ErrorMessage);
}
}
}
}
Besides I found an awesome video about removing ad after IAP and you can also refer to it.
This was an issue with AdDuplex Ad Control and was fixed in version 9.0.0.13.
Note: don't forget to set IsTest to false to see the "production" behaviour.

WPF MVVM: How to close a window

I have a Button that closes my window when it's clicked:
<Button x:Name="buttonOk" IsCancel="True">Ok</Button>
That's fine until I add a Command to the Button i.e.
<Button x:Name="buttonOk"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
Now it doesn't close presumably because I am handling the Command. I can fix this by putting an EventHandler in and calling this.Close() i.e.
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
but now I have code in my code behind i.e. the method SaveCommand. I am using the MVVM pattern and SaveCommand is the only code in my code behind.
How can I do this differently so as not to use code behind?
I just completed a blog post on this very topic. In a nutshell, add an Action property to your ViewModel with get and set accessors. Then define the Action from your View constructor. Finally, invoke your action in the bound command that should close the window.
In the ViewModel:
public Action CloseAction { get; set;}
and in the View constructor:
private View()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
if ( vm.CloseAction == null )
vm.CloseAction = new Action(this.Close);
}
Finally, in whatever bound command that should close the window, we can simply invoke
CloseAction(); // Calls Close() method of the View
This worked for me, seemed like a fairly elegant solution, and saved me a bunch of coding.
Very clean and MVVM way is to use InteractionTrigger and CallMethodAction defined in Microsoft.Interactivity.Core
You will need to add a new namespace as below
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
You will need the Microsoft.Xmal.Behaviours.Wpf assembly and then the below xaml code will work.
<Button Content="Save" Command="{Binding SaveCommand}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction MethodName="Close"
TargetObject="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
You don't need any code behind or anything else and can also call any other method of Window.
As someone commented, the code I have posted is not MVVM friendly, how about the second solution?
1st, not MVVM solution (I will not delete this as a reference)
XAML:
<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
ViewModel:
public ICommand OkCommand
{
get
{
if (_okCommand == null)
{
_okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
}
return _okCommand ;
}
}
void DoOk(Window win)
{
// Your Code
win.DialogResult = true;
win.Close();
}
bool CanDoOk(Window win) { return true; }
2nd, probably better solution:
Using attached behaviours
XAML
<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />
View Model
public ICommand OkCommand
{
get { return _okCommand; }
}
Behaviour Class
Something similar to this:
public static class CloseOnClickBehaviour
{
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(CloseOnClickBehaviour),
new PropertyMetadata(false, OnIsEnabledPropertyChanged)
);
public static bool GetIsEnabled(DependencyObject obj)
{
var val = obj.GetValue(IsEnabledProperty);
return (bool)val;
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void OnIsEnabledPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
{
var button = dpo as Button;
if (button == null)
return;
var oldValue = (bool)args.OldValue;
var newValue = (bool)args.NewValue;
if (!oldValue && newValue)
{
button.Click += OnClick;
}
else if (oldValue && !newValue)
{
button.PreviewMouseLeftButtonDown -= OnClick;
}
}
static void OnClick(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button == null)
return;
var win = Window.GetWindow(button);
if (win == null)
return;
win.Close();
}
}
I'd personally use a behaviour to do this sort of thing:
public class WindowCloseBehaviour : Behavior<Window>
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CloseButtonProperty =
DependencyProperty.Register(
"CloseButton",
typeof(Button),
typeof(WindowCloseBehaviour),
new FrameworkPropertyMetadata(null, OnButtonChanged));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public Button CloseButton
{
get { return (Button)GetValue(CloseButtonProperty); }
set { SetValue(CloseButtonProperty, value); }
}
private static void OnButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = (Window)((WindowCloseBehaviour)d).AssociatedObject;
((Button) e.NewValue).Click +=
(s, e1) =>
{
var command = ((WindowCloseBehaviour)d).Command;
var commandParameter = ((WindowCloseBehaviour)d).CommandParameter;
if (command != null)
{
command.Execute(commandParameter);
}
window.Close();
};
}
}
You can then attach this to your Window and Button to do the work:
<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication6"
Title="Window1" Height="300" Width="300">
<i:Interaction.Behaviors>
<local:WindowCloseBehaviour CloseButton="{Binding ElementName=closeButton}"/>
</i:Interaction.Behaviors>
<Grid>
<Button Name="closeButton">Close</Button>
</Grid>
</Window>
I've added Command and CommandParameter here so you can run a command before the Window closes.
For small apps, I use my own Application Controller for showing, closing and disposing windows and DataContexts. It's a central point in UI of an application.
It's something like this:
//It is singleton, I will just post 2 methods and their invocations
public void ShowNewWindow(Window window, object dataContext = null, bool dialog = true)
{
window.DataContext = dataContext;
addToWindowRegistry(dataContext, window);
if (dialog)
window.ShowDialog();
else
window.Show();
}
public void CloseWindow(object dataContextSender)
{
var correspondingWindows = windowRegistry.Where(c => c.DataContext.Equals(dataContextSender)).ToList();
foreach (var pair in correspondingWindows)
{
pair.Window.Close();
}
}
and their invocations from ViewModels:
// Show new Window with DataContext
ApplicationController.Instance.ShowNewWindow(
new ClientCardsWindow(),
new ClientCardsVM(),
false);
// Close Current Window from viewModel
ApplicationController.Instance.CloseWindow(this);
Of course you can find some restrictions in my solution. Again: I use it for small projects, and it's enough. If you're interested, I can post full code here or somewhere else/
I've tried to resolve this issue in some generic, MVVM way, but I always find that I end up unnecessary complex logic. To achieve close behavior I have made an exception from the rule of no code behind and resorted to simply using good ol' events in code behind:
XAML:
<Button Content="Close" Click="OnCloseClicked" />
Code behind:
private void OnCloseClicked(object sender, EventArgs e)
{
Visibility = Visibility.Collapsed;
}
Although I wish this would be better supported using commands/MVVM, I simply think that there is no simpler and more clear solution than using events.
I use the Publish Subscribe pattern for complicated class-dependencies:
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
CloseComand = new DelegateCommand((obj) =>
{
MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
});
}
}
Window:
public partial class SomeWindow : Window
{
Subscription _subscription = new Subscription();
public SomeWindow()
{
InitializeComponent();
_subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
{
this.Close();
});
}
}
You can leverage Bizmonger.Patterns to get the MessageBus.
MessageBus
public class MessageBus
{
#region Singleton
static MessageBus _messageBus = null;
private MessageBus() { }
public static MessageBus Instance
{
get
{
if (_messageBus == null)
{
_messageBus = new MessageBus();
}
return _messageBus;
}
}
#endregion
#region Members
List<Observer> _observers = new List<Observer>();
List<Observer> _oneTimeObservers = new List<Observer>();
List<Observer> _waitingSubscribers = new List<Observer>();
List<Observer> _waitingUnsubscribers = new List<Observer>();
int _publishingCount = 0;
#endregion
public void Subscribe(string message, Action<object> response)
{
Subscribe(message, response, _observers);
}
public void SubscribeFirstPublication(string message, Action<object> response)
{
Subscribe(message, response, _oneTimeObservers);
}
public int Unsubscribe(string message, Action<object> response)
{
var observers = new List<Observer>(_observers.Where(o => o.Respond == response).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Respond == response));
observers.AddRange(_oneTimeObservers.Where(o => o.Respond == response));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public int Unsubscribe(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Subscription == subscription));
observers.AddRange(_oneTimeObservers.Where(o => o.Subscription == subscription));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public void Publish(string message, object payload)
{
_publishingCount++;
Publish(_observers, message, payload);
Publish(_oneTimeObservers, message, payload);
Publish(_waitingSubscribers, message, payload);
_oneTimeObservers.RemoveAll(o => o.Subscription == message);
_waitingUnsubscribers.Clear();
_publishingCount--;
}
private void Publish(List<Observer> observers, string message, object payload)
{
Debug.Assert(_publishingCount >= 0);
var subscribers = observers.Where(o => o.Subscription.ToLower() == message.ToLower());
foreach (var subscriber in subscribers)
{
subscriber.Respond(payload);
}
}
public IEnumerable<Observer> GetObservers(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription));
return observers;
}
public void Clear()
{
_observers.Clear();
_oneTimeObservers.Clear();
}
#region Helpers
private void Subscribe(string message, Action<object> response, List<Observer> observers)
{
Debug.Assert(_publishingCount >= 0);
var observer = new Observer() { Subscription = message, Respond = response };
if (_publishingCount == 0)
{
observers.Add(observer);
}
else
{
_waitingSubscribers.Add(observer);
}
}
#endregion
}
}
Subscription
public class Subscription
{
#region Members
List<Observer> _observerList = new List<Observer>();
#endregion
public void Unsubscribe(string subscription)
{
var observers = _observerList.Where(o => o.Subscription == subscription);
foreach (var observer in observers)
{
MessageBus.Instance.Unsubscribe(observer.Subscription, observer.Respond);
}
_observerList.Where(o => o.Subscription == subscription).ToList().ForEach(o => _observerList.Remove(o));
}
public void Subscribe(string subscription, Action<object> response)
{
MessageBus.Instance.Subscribe(subscription, response);
_observerList.Add(new Observer() { Subscription = subscription, Respond = response });
}
public void SubscribeFirstPublication(string subscription, Action<object> response)
{
MessageBus.Instance.SubscribeFirstPublication(subscription, response);
}
}
There is a useful behavior for this task which doesn't break MVVM, a Behavior, introduced with Expression Blend 3, to allow the View to hook into commands defined completely within the ViewModel.
This behavior demonstrates a simple technique for allowing the
ViewModel to manage the closing events of the View in a
Model-View-ViewModel application.
This allows you to hook up a behavior in your View (UserControl) which
will provide control over the control's Window, allowing the ViewModel
to control whether the window can be closed via standard ICommands.
Using Behaviors to Allow the ViewModel to Manage View Lifetime in M-V-VM
http://gallery.expression.microsoft.com/WindowCloseBehavior/
Above link has been archived to http://code.msdn.microsoft.com/Window-Close-Attached-fef26a66#content
I struggled with this topic for some time, and eventually went with the simplest approach that is still consistent with MVVM: Have the button execute the Command that does all the heavy lifting and have the button's Click handler close the window.
XAML
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}" />
XAML.cs
public void closeWindow()
{
this.DialogResult = true;
}
SaveCommand.cs
// I'm in my own file, not the code-behind!
True, there is still code-behind, but there isn't anything inherently bad about that. And it makes the most sense to me, from an OO perspective, to just tell the window to close itself.
We have the name property in the .xaml definition:
x:Name="WindowsForm"
Then we have the button:
<Button Command="{Binding CloseCommand}"
CommandParameter="{Binding ElementName=WindowsForm}" />
Then in the ViewModel:
public DelegateCommand <Object> CloseCommand { get; private set; }
Constructor for that view model:
this.CloseCommand = new DelegateCommand<object>(this.CloseAction);
Then at last, the action method:
private void CloseAction (object obj)
{
Window Win = obj as Window;
Win.Close();
}
I used this code to close a pop-up window from an application..
I found myself having to do this on a WPF application based on .Net Core 3.0, where unfortunately behaviour support was not yet officially available in the Microsoft.Xaml.Behaviors.Wpf NuGet package.
Instead, I went with a solution that made use of the Façade design pattern.
Interface:
public interface IWindowFacade
{
void Close();
}
Window:
public partial class MainWindow : Window, IWindowFacade
…
Standard command property on the view model:
public ICommand ExitCommand
…
Control binding:
<MenuItem Header="E_xit" Command="{Binding ExitCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
Command:
public class ExitCommand : ICommand
{
…
public void Execute(object parameter)
{
var windowFacade = parameter as IWindowFacade;
windowFacade?.Close();
}
…
}
Because the Close() method is already implemented by the Window class, applying the façade interface to the window is the only required code behind in the UI layer (for this simple example). The command in the presentation layer avoids any dependencies on the view/UI layer as it has no idea what it is talking to when it calls the Close method on the façade.
In your current window xaml.cs file, call the below code:
var curWnd = Window.GetWindow(this); // passing current window context
curWnd?.Close();
This should do the thing.
It worked for me, hope will do the same for you )
I have following solution in Silverlight. Would also be in WPF.
ChildWindowExt.cs:
namespace System.Windows.Controls
{
public class ChildWindowExt : ChildWindow
{
public static readonly DependencyProperty IsOpenedProperty =
DependencyProperty.Register(
"IsOpened",
typeof(bool),
typeof(ChildWindowExt),
new PropertyMetadata(false, IsOpenedChanged));
private static void IsOpenedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == false)
{
ChildWindowExt window = d as ChildWindowExt;
window.Close();
}
else if ((bool)e.NewValue == true)
{
ChildWindowExt window = d as ChildWindowExt;
window.Show();
}
}
public bool IsOpened
{
get { return (bool)GetValue(IsOpenedProperty); }
set { SetValue(IsOpenedProperty, value); }
}
protected override void OnClosing(ComponentModel.CancelEventArgs e)
{
this.IsOpened = false;
base.OnClosing(e);
}
protected override void OnOpened()
{
this.IsOpened = true;
base.OnOpened();
}
}
}
ItemWindow.xaml:
<extControls:ChildWindowExt
x:Class="MyProject.ItemWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:extControls="clr-namespace:System.Windows.Controls"
Title="{Binding Title}" IsOpened="{Binding IsOpened, Mode=TwoWay}" Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<Button Command="{Binding UpdateCommand}" Content="OK" Width="70" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</extControls:ChildWindowExt>
ItemViewModel.cs:
private bool _IsOpened;
public bool IsOpened
{
get
{
return _IsOpened;
}
set
{
if (!Equals(_IsOpened, value))
{
_IsOpened = value;
RaisePropertyChanged("IsOpened");
}
}
}
private RelayCommand _UpdateCommand;
/// <summary>
/// Insert / Update data entity
/// </summary>
public RelayCommand UpdateCommand
{
get
{
if (_UpdateCommand == null)
{
_UpdateCommand = new RelayCommand(
() =>
{
// Insert / Update data entity
...
IsOpened = false;
},
() =>
{
return true;
});
}
return _UpdateCommand;
}
}
ItemsViewModel.cs:
private RelayCommand _InsertItemCommand;
/// <summary>
///
/// </summary>
public RelayCommand InsertItemCommand
{
get
{
if (_InsertItemCommand == null)
{
_InsertItemCommand = new RelayCommand(
() =>
{
ItemWindow itemWin = new ItemWindow();
itemWin.DataContext = new ItemViewModel();
itemWin.Show();
// OR
// ItemWindow itemWin = new ItemWindow();
// ItemViewModel newItem = new ItemViewModel();
// itemWin.DataContext = newItem;
// newItem.IsOpened = true;
},
() =>
{
return true;
});
}
return _InsertItemCommand;
}
}
MainPage.xaml:
<Grid x:Name="LayoutRoot">
<Button Command="{Binding InsertItemCommand}" Content="Add New" Width="70" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>
I wish you all good ideas and projects ;-)
This might helps you, closing a wpf window using mvvm with minimal code behind: http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-code-behind/
I think the most simple way has not been included already (almost). Instead of using Behaviours which adds new dependencies just use attached properties:
using System;
using System.Windows;
using System.Windows.Controls;
public class DialogButtonManager
{
public static readonly DependencyProperty IsAcceptButtonProperty = DependencyProperty.RegisterAttached("IsAcceptButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsAcceptButtonPropertyChanged));
public static readonly DependencyProperty IsCancelButtonProperty = DependencyProperty.RegisterAttached("IsCancelButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsCancelButtonPropertyChanged));
public static void SetIsAcceptButton(UIElement element, bool value)
{
element.SetValue(IsAcceptButtonProperty, value);
}
public static bool GetIsAcceptButton(UIElement element)
{
return (bool)element.GetValue(IsAcceptButtonProperty);
}
public static void SetIsCancelButton(UIElement element, bool value)
{
element.SetValue(IsCancelButtonProperty, value);
}
public static bool GetIsCancelButton(UIElement element)
{
return (bool)element.GetValue(IsCancelButtonProperty);
}
private static void OnIsAcceptButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Button button = sender as Button;
if (button != null)
{
if ((bool)e.NewValue)
{
SetAcceptButton(button);
}
else
{
ResetAcceptButton(button);
}
}
}
private static void OnIsCancelButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Button button = sender as Button;
if (button != null)
{
if ((bool)e.NewValue)
{
SetCancelButton(button);
}
else
{
ResetCancelButton(button);
}
}
}
private static void SetAcceptButton(Button button)
{
Window window = Window.GetWindow(button);
button.Command = new RelayCommand(new Action<object>(ExecuteAccept));
button.CommandParameter = window;
}
private static void ResetAcceptButton(Button button)
{
button.Command = null;
button.CommandParameter = null;
}
private static void ExecuteAccept(object buttonWindow)
{
Window window = (Window)buttonWindow;
window.DialogResult = true;
}
private static void SetCancelButton(Button button)
{
Window window = Window.GetWindow(button);
button.Command = new RelayCommand(new Action<object>(ExecuteCancel));
button.CommandParameter = window;
}
private static void ResetCancelButton(Button button)
{
button.Command = null;
button.CommandParameter = null;
}
private static void ExecuteCancel(object buttonWindow)
{
Window window = (Window)buttonWindow;
window.DialogResult = false;
}
}
Then just set it on your dialog buttons:
<UniformGrid Grid.Row="2" Grid.Column="1" Rows="1" Columns="2" Margin="3" >
<Button Content="Accept" IsDefault="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsAcceptButton="True" />
<Button Content="Cancel" IsCancel="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsCancelButton="True" />
</UniformGrid>
I also had to deal with this problem, so here my solution. It works great for me.
1. Create class DelegateCommand
public class DelegateCommand<T> : ICommand
{
private Predicate<T> _canExecuteMethod;
private readonly Action<T> _executeMethod;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<T> executeMethod) : this(executeMethod, null)
{
}
public DelegateCommand(Action<T> executeMethod, Predicate<T> canExecuteMethod)
{
this._canExecuteMethod = canExecuteMethod;
this._executeMethod = executeMethod ?? throw new ArgumentNullException(nameof(executeMethod), "Command is not specified.");
}
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
CanExecuteChanged(this, null);
}
public bool CanExecute(object parameter)
{
return _canExecuteMethod == null || _canExecuteMethod((T)parameter) == true;
}
public void Execute(object parameter)
{
_executeMethod((T)parameter);
}
}
2. Define your command
public DelegateCommand<Window> CloseWindowCommand { get; private set; }
public MyViewModel()//ctor of your viewmodel
{
//do something
CloseWindowCommand = new DelegateCommand<Window>(CloseWindow);
}
public void CloseWindow(Window win) // this method is also in your viewmodel
{
//do something
win?.Close();
}
3. Bind your command in the view
public MyView(Window win) //ctor of your view, window as parameter
{
InitializeComponent();
MyButton.CommandParameter = win;
MyButton.Command = ((MyViewModel)this.DataContext).CloseWindowCommand;
}
4. And now the window
Window win = new Window()
{
Title = "My Window",
Height = 800,
Width = 800,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
};
win.Content = new MyView(win);
win.ShowDialog();
so thats it, you can also bind the command in the xaml file and find the window with FindAncestor and bind it to the command parameter.
I've been searching for a solution to the same problem and found that doing following works fine. The solution is similar to what OP has mentioned in his question with some differences:
No need of IsCancel property.
Code behind should not close window. Just set DialogResult
In my case it first executes code behind and then view model command bound to the button.
XAML
<Button x:Name="buttonOk" Click="Save_Click" Command="{Binding SaveCommand}">OK</Button>
Code Behind
private void Apply_OnClick(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
View Model
private void Save()
{
// Save data.
}
Hope this helps.
You could rephrase the question, and by doing so - coming up with another solution.
How can I enable communication between views, viewmodels and whatnot in an MVVM environment?
You could use the Mediator pattern. It's basically a notification system. For the actual Mediator implementation, google for it or ask me and I can email it.
Make a Command whose purpose is to close the view.
public void Execute( object parameter )
{
this.viewModel.DisposeMyStuff();
Mediator.NotifyColleagues(Mediator.Token.ConfigWindowShouldClose);
}
The Mediator will raise a notification (a token)
Listen to this notification (token) like this in the View codebehind constructor:
public ClientConfigView()
{
InitializeComponent();
Mediator.ListenOn(Mediator.Token.ConfigWindowShouldClose, callback => this.Close() );
}
The solution to close a window in wpf that that worked for me is not answered here so i thought i would add my solution too.
private static Window GetWindow(DependencyObject sender)
{
Window window = null;
if (sender is Window)
window = (Window)sender;
if (window == null)
window = Window.GetWindow(sender);
return window;
}
private void CloseWindow(object sender, RoutedEventArgs e)
{
var button = (Button)sender as DependencyObject;
Window window = GetWindow(button);
if (window != null)
window.Close();
// window.Visibility = Visibility.Hidden;
// choose between window.close or set window.visibility to close or hide the window.
// }
}
Add CloseWindow event to the button in you window as following.
<Button Content="Cancel" Click="CloseWindow" >
Simple approach is close window on saveComand Implementation.
Use below code to close window.
Application.Current.Windows[1].Close();
It will close the child window.
Without any dependencies.
<Window ...>
...
<Button Command="{x:Static SystemCommands.CloseWindowCommand}" />
</Window>
You can do it without code behind. Create command, in Execute method call "Save" method on viewmodel and after that call close method on edit window, which you can pass to the command by parameter:
public void Execute(object parameter)
{
_mainViewModel.SaveSomething();
var editWindow = parameter as MyEditWindow;
editWindow?.Close();
}
Save&Close button XAML:
<Button Content"Save&Close" Command="{Binding SaveCmd}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" IsDefault="True" />

Categories