I hard to work for my own transition animation in Frame. But following mysterious problem hit me with deadly deadly damage.
First, here is a sample window. It consists of one frame and one button. Of course it differs to my trunk project, nevertheless, it still showed same problem.
When I clicked the button, the frame navigates to Page1.
EDITED : here is source state of Page1. It was taken in VS2010 IDE.
So, what is problem? - problem is occurring when I call RenderToBitmap() to getting the visual of the destination page. (In this case, destination is the Page1.)
I call RenderToBitmap() with the following code. Here is all of MainWindow's code behind.
public partial class MainWindow : Window
{
public MainWindow() { InitializeComponent(); }
Page1 pg1 = new Page1();
private void Button_Click(object sender, RoutedEventArgs e)
{
this.frame.Navigate(pg1); //start to navigate.
}
private void frame_Navigated(object sender, NavigationEventArgs e)
{
//SaveVisualToPng is my own static method.
WPFHelper.SaveVisualToPng("d:\\a.png", pg1);
}
}
And finally, here is a state of visual when frame_Navigated is called. The TextBox and the TextBlock was rendered, and CheckBox and RadioButton also Rendered. Mysteriously, but, CheckBox and RadioButtons' checked state (notching image and ellipse image) isn't rendered yet.
How can I deal this hell forged problem? Of cause this is a very bit things. But I think this part is a basement of my application, so I want to make flawlessly.
EDITED(2) here is a source of SaveVisualToPng(). Originally it contains a some methods to help work, like GetDPI, but i omitted that. Instead following code make a same result.
public static class WPFHelper
{
public static void SaveVisualToPng(string path, Visual v)
{
int width = Convert.ToInt32(((FrameworkElement)v).ActualWidth);
int height = Convert.ToInt32(((FrameworkElement)v).ActualHeight);
RenderTargetBitmap myBmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
myBmp.Render(v);
if (myBmp != null)
{
PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(myBmp));
using (Stream stm = System.IO.File.Create(path))
{
png.Save(stm);
}
}
}
}
See if queuing your work on the dispatcher (to give WPF time to render the controls) helps:
private void frame_Navigated(object sender, NavigationEventArgs e)
{
Action save = () => WPFHelper.SaveVisualToPng("d:\\a.png", pg1);
Dispatcher.BeginInvoke(DispatcherPriority.Background, save);
}
Without your WPFHelper implementation I can't test it, but it's worth a try.
Related
I am new to WPF and want to build an application which will do serial communication with the my driver and from program i can set values to the driver.
I have managed to make a UI as shown in this figure here . If i press Blue View as pointed by the arrow at last, the view of my window is like this. If i press the Red View Option then the display is like this
Setting button is where the arrow pointing at top right corner(below the close button of window) and when pressed my window will look like this.
Basically i am changing the BIG RECTANGLE content according to the button i have pressed for example (rectangle is blue when Blue view is clicked, rectangle filled up with Red and one label and a button to change the label when Red View is clicked)
So now my problem is I cannot retain the value i had set in this BIG RECTANGLE after i change the content of this BIG RECTANGLE. For example when i pressed the setting button and change the setting like this I am ready for communication in COM5 and the option to close the port can be pressed. Now before closing the port if I change the view of the BIG RECTANGLE by pressing Red View or Blue View then after press the setting button then i don't have that option to close port anymore and since I had already opened the com5 port earlier so when i try to open the port it will also give me error.
Please Help me with this. My visual studio solution explorer looks like this and My code in the button clicked event are as follows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SerialPortOnOFFButton_Clicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("ON OFF Clicked");
}
private void SerialPortSettingButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new SerialPortSettingView();
}
private void RedViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new Redview();
}
private void BlueViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new Blueview();
}
}
And my Solution explorer looks like this
If you want to just close your port you can implement IDisposable interface in your SerialPortSettingView class like this. Each time you change DataContext from SerialPortSettingView your port will be closed.
public class SerialPortSettingView : IDisposable
{
private FileStream _fileStream;
public SerialPortSettingView()
{
_fileStream = new FileStream("somefile.txt", FileMode.Open);
}
public void Dispose()
{
_fileStream?.Close();
}
}
Each time you change the view you are creating a new object to set the DataContext to, if you kept the individual objects as private fields you could then simply set the DataContext to these:
private SerialPortSettingView _serialPortSettingView;
private RedView _redView;
private BlueView _blueView;
public MainWindow()
{
_serialPortSettingView = new SerialPortSettingView();
_redView = new RedView();
_blueView = new BlueView();
InitializeComponent();
}
private void SerialPortOnOFFButton_Clicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("ON OFF Clicked");
}
private void SerialPortSettingButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = _serialPortSettingView;
}
private void RedViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = _redview;
}
private void BlueViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = _blueview;
}
This way when you switch between views you'll be using the stored version and when you change values they will be stored in that View.
If I were doing this solution I would change the ContentControl to a TabControl (hide the headers) then create each View, with an accompanying ViewModel, as tabs. Then as each click event is fired you simply set the .SelectedIndex property of the TabControl. I'd create a MainWindowViewModel and set the MainWindow's DataContext to this in the constructor:
private MainWindowViewModel = new MainWindowViewModel();
public MainWindow()
{
DataContext = _mainWindowViewModel;
}
And put all the logic in the MainWindowViewModel (you need to use commands). Using code behind isn't what WPF was meant for, you can read up on it all here with a good tutorial to follow - MVVM Tutorial
I don't want to over complicate things and stuff you with too much information in one go but if you start off doing things this way it will be better, hope this helps.
I'm trying to find my relative coordinate for a usercontrol inside my mainwindow. I tried using the "Control.PointToScreen()" method but with no luck. Everytime I do this i get an exception that says:
System.InvalidOperationException: This Visual is not connected to a PresentationSource
I think this has something to do with me calling pointToScreen before the visuals have rendered properly, since i'm already calling the method in my Mains Constructor.
Anyways, I would like to hear if anyone of you had a hint/Solution/Idea how I could perhaps work around this.
Just to clearify what i'm trying to do, my control contains a photocontrol which i need the exact location off inside my maincontrol, since i want to use these coordinates to create a duplicate of the control on top of it
Experimenting with PointToScreen.
In your code register for the Loaded event of the UserControl.
That should fix the bug that the visuals have not been rendered yet when you try to get the position.
YourControl.Loaded += ControlLoaded;
public void ControlLoaded(object sender, EventArgs e){
Console.WriteLine(YourControl.PointToScreen(new Point(0,0));
}
Edit
Since you want the position of your control relative to your window, better try that one .
YourControl.TransformToAncestor(YourWindow).Transform(new Point(0,0))
Since Contend rendered didn't work I found a solution for my problem.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ItemsControl ItemsControl = UCEnvironmentControl.GetItemsControlPhotos();
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => Control.PointToScreen(new Point(0,0));
}
This way, it fires in the LoadedEvent, but waits for the content to be rendered, and then finally it gives you your coordinate back and sets your control
The solution from Max Mazur is working for me:
public partial class MainWindow : Window
{
Point locationOfYourControl;
public MainWindow()
{
InitializeComponent();
this.ContentRendered += MainWindow_ContentRendered;
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
locationOfYourControl = YourControl.PointToScreen(new Point(0, 0));
}
}
I have a Windows 8.1 C# app that shows a page with a pretty big textbox (covering almost all of the page; its a writing app).
When the on screen keyboard shows up, it overlays half of the textbox.
I would like to resize the textbox (or even the whole page) so it isnt covered by the keyboard.
I am now thrying to achieve this using the static InputPane and subscribing to its showing and hiding events. I then try to change the margins of my textbox using the occuled rectangle provided in the eventargs. This works, but since my page is still the height of the screen it scrolls it halfway to the bottom.
public MainPage()
{
var inputPane = InputPane.GetForCurrentView();
inputPane.Showing += this.InputPaneShowing;
inputPane.Hiding += this.InputPaneHiding;
}
void InputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
this.EditBox.Margin = new Thickness();
}
private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
this.EditBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
}
While trying this out I'm getting the feeling this isnt the ideal solution, but I havent got a better idea. Ideally I'd think it would be something like the vertical split when you have to apps opened, but then horizontally with the keyboard at the bottom and the app only the size available above the keyboard.
Any idea if this is possible?
Since you are handling the interface occlusion yourself you should set the property EnsuredFocusedElementInView from InputPane.Showing eventargs to true. It would prevent interface from automatically scrolling.
private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
args.EnsuredFocusedElementInView = true;
this.EditBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
};
I am working on a personal project in winforms just to gain some experience in it since I've never had the chance to work with it before. So, I'm quite the n00b when it comes to Winforms. This is the error I'm encountering:
In form BudgetTracker, I have a button called 'AddCat'. Below is the form's constructor and the button's click eventHandler:
public form_BudgetTracker()
{
InitializeComponent();
setEvents();
}
public void setEvents()
{
this.btn_AddCat.Click += new System.EventHandler(this.btn_AddCat_Click);
}
private void btn_AddCat_Click(object sender, EventArgs e)
{
form_NewCat NewCatForm = new form_NewCat();
var NewCatFormResult = NewCatForm.ShowDialog();
NewCatForm.Show();
}
In the NewCat form that comes up, I have a Cancel button. Code:
public form_NewCat()
{
InitializeComponent();
SetEvents();
}
private void SetEvents()
{
this.btn_Add.Click += new System.EventHandler(this.btn_Add_Click);
this.btn_Cancel.Click += new System.EventHandler(this.btn_Cancel_Click);
}
private void btn_Cancel_Click(object sender, EventArgs e)
{
this.Close();
}
The problem I'm facing is, when I click Add, the new form comes up. At this point, if I click Cancel, the form disappears but instantly a new instance of the form appears. I then click cancel again, and the form disappears.
What part of my code is making the form appear twice. I checked the contructors etc, but couldn't figure it out. Any help or pointers would be appreciated.
PS - As I mentioned, I'm new to winforms programming, so any cues or pointers would be appreciated as well.
private void btn_AddCat_Click(object sender, EventArgs e)
{
form_NewCat NewCatForm = new form_NewCat();
var NewCatFormResult = NewCatForm.ShowDialog(); // <-- opens the first time
NewCatForm.Show(); // <-- opens the second time
}
Judging from your code, you're simply showing the form twice!!!
form_NewCat NewCatForm = new form_NewCat();
var NewCatFormResult = NewCatForm.ShowDialog();
NewCatForm.Show();
The second line shows the form and blocks the method until DialogResult is set, then the third line shows the form without blocking the method.
Simply remove the third line!
Try stepping through the code using the F8 key instead of running it, or hitting F5. It will show you line by line what it's about to execute.
delete NewCatForm.Show();
OR - How To Shave A Koala To Stop It Looking Squashed. (But I didn't think that would make a suitably techy title)
The Problem: You have three preview images derived from a main image. The preview images are resized for standardised picture spaces on a company website, the main image can be any size image from anywhere.
Example: The main image is a hi-res image of a koala bear measuring 2000x2250. Your previews want to render the koala at 200x200, 200x50 and 250x150.
Your utility program resizes and stretches the original image to the size of your three "actual size" previews but obviously each preview looks a bit squashy and you know everyone hates to see a squashed koala.
To resolve this you add a little cropping method to your program which shaves five pixels from the preview on the desired side. This means you should be able to resize your image and unsquash your koala by shaving off the unnecessary parts of the image.
You add four buttons to each preview image picture box and create four generic methods for sending the correct shaving instructions to the crop method. You want to associate each specific button with a specific picturebox on the form, but you want to send all the click events to four generic functions.
How do you tell the generic function which of the three preview picturebox images you want it to shave in an elegant and wonderful way?
Example Code:
//cropPict=method for cropping the picture in the relevant picturebox.
//CropSide=a little enum which tells the method which side to crop.
private void btnT_Click(object sender, EventArgs e)
{
cropPict(/*Reference to PictureBox Goes Here*/, CropSide.Top);
}
private void btnB_Click(object sender, EventArgs e)
{
cropPict(/*Reference to PictureBox Goes Here*/, CropSide.Bottom);
}
private void btnR_Click(object sender, EventArgs e)
{
cropPict(/*Reference to PictureBox Goes Here*/, CropSide.Right);
}
private void btnL_Click(object sender, EventArgs e)
{
cropPict(/*Reference to PictureBox Goes Here*/, CropSide.Left);
}
EDIT: As it happens, inspired by Hans below, rather than just stuffing the PictureBox into the tag. Which was a great idea I actually put a KeyValuePair into the tag for each button like so:
btnCCB.Tag = new KeyValuePair<CropSide,PictureBox>(CropSide.Bottom,pbxKoala);
btnCCL.Tag = new KeyValuePair<CropSide, PictureBox>(CropSide.Left, pbxKoala);
btnCCR.Tag = new KeyValuePair<CropSide, PictureBox>(CropSide.Right, pbxKoala);
btnCCT.Tag = new KeyValuePair<CropSide, PictureBox>(CropSide.Top, pbxKoala);
Then I could just wire all the buttons up to a single event handler like so:
private void btnC_Click(object sender, EventArgs e)
{
Button btnSend = (Button)sender;
KeyValuePair<CropSide, PictureBox> kvCrop = (KeyValuePair<CropSide, PictureBox>)btnSend.Tag;
cropPict(kvCrop.Value,kvCrop.Key);
}
Of course, there's still plenty more to do but that pretty much sorted out my problem. Thanks Hans!
Use the Button.Tag property to store a reference to its associated PictureBox. Cast sender to Button:
public Form1()
{
InitializeComponent();
button1.Tag = pictureBox1;
button1.Click += btnT_Click;
// etc..
}
private void btnT_Click(object sender, EventArgs e)
{
var btn = (Button)sender;
cropPict((PictureBox)btn.Tag, CropSide.Top);
}