open wpf window in Revit as add ins - wpf

I made a project using WPF and I want to open its window in Revit I trying windows form it worked but wpf not opened !!
I use this
public virtual Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
IDesign.MainWindow testsd = new IDesign.MainWindow();
testsd.InitializeComponent();
//MessageBox.Show("notworking");
return Result.Succeeded;
}
But it didn't work any solutions

public virtual Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
IDesign.MainWindow testsd = new IDesign.MainWindow();
//testsd.InitializeComponent();
//MessageBox.Show("notworking");
return Result.Succeeded;
}
public MainWindow()
{
InitializeComponent();
this.ShowDialog();
}

You may want to create a WPF User Control and assign it as the Content of a WPF window in a Revit External Command, something like:
UserControl1 userControl = new UserControl1();
Window win = new Window();
win.Content = userControl;
win.Show();

Related

Draw large DrawingVisual in Windows Forms

Background
I have a large report in my WinForms application that is generated into a WPF DrawingVisual. I want to display the report in a separate resizable window that enables the users to scroll up/down as they read it. In this case the report doesn't have any page breaks and consist of one large page.
First attempt:
Use a customized DocumentViewer to display the report.
Added the DrawingVisual into a FixedDocument with a custom height, added the FixedDocument into a FixedDocumentSequence and finally passed the document sequence into the print preview window (that uses a custom DocumentViewer).
var previewWindow = new ReportPrintPreview(docSeq);
previewWindow.Show();
Classes:
public class ReportPrintPreview : Window
{
private readonly DocumentViewer docViewer;
public ReportPrintPreview(IDocumentPaginatorSource doc)
{
docViewer = new CustomDocumentViewer();
docViewer.Document = doc;
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
AddChild(docViewer);
}
}
public class CustomDocumentViewer : DocumentViewer
{
public CustomDocumentViewer()
{
ShowPageBorders = false;
}
protected override void OnPrintCommand()
{
}
}
This works and gives good performance for large reports, but I find the customization possibilities of the DocumentViewer limiting. Want to add combo boxes in the toolbar and control the zoom of the report when the window is resized for example.
Second attempt:
Use a regular Windows Forms form and draw the DrawingVisual using a ElementHost. The element host is placed inside a panel to enable scrolling.
var form = new ReportViewer(visual, pSize);
form.ShowDialog();
Classes:
public partial class ReportViewer : System.Windows.Forms.Form
{
public ReportViewer(DrawingVisual visual, Size size)
{
InitializeComponent();
var wpfPanel = new WpfDrawingUserControl(visual);
elementHost1.Width = size.Width;
elementHost1.Height = size.Height;
elementHost1.Child = wpfPanel;
}
}
public class WpfDrawingUserControl : System.Windows.Controls.UserControl
{
public WpfDrawingUserControl(DrawingVisual visual)
{
var image = new Image();
image.Source = new DrawingImage(visual.Drawing);
Content = image;
}
}
I create a UserControl that contains an image that is created from the DrawingGroup of the DrawingVisual.
This kind of works, but the performance when scrolling is bad. And it even crashes when the report is large enough.
Solution?
How can I do more effective drawing of the DrawingVisual in the form? I guess the whole DrawingVisual will be painted in the UserControl even when just a part of the report is showing in the scroll enabled panel.
Alternatively, how can I customize the DocumentViewer or create my own WPF control to display the DrawingVisual.

Opening a new dialog using WPF with MVVM

I am currently using MVVM (Light) to build an application with WPF. However, in a few cases I must open a new dialog (also WPF) when the user clicks a button. However, this is being a tough fight.
Here is how I am doing it:
private void _ShowItemDialog(Item item)
{
var itemVM = new ItemViewModel();
itemVM.CurrentItem = item ?? new Item();
itemVM.Load();
var itemView = new View.ItemView() { DataContext = itemVM };
if (itemView.ShowDialog() == true)
{
if (item == null)
{
itemList.Add(itemVM.CurrentItem);
}
}
itemVM.Cleanup();
}
And the itemView XAML there is no binding to the DataContext, otherwise two different instances of the ViewModel would be created.
Inside the Window tag. To have the result at ShowDialog, I use the DialogCloser code:
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
In the ItemView, this is declared inside Window tag as follows:
my:DialogCloser.DialogResult="{Binding DialogResult}"
And when the dialog is closed, the closing event sets DialogResult to true or false.
This works perfectly for the first time the screen is opened, but it is not possible to open the dialog again after it is closed.
I would like to know if you have any better ideas for opening the dialog, and why this code does not work.
Thanks!
EDIT:
I have already fixed the code. What I need to do is create a new ViewModel and attach it to the DataContext every time the dialog is opened. Moreover, I had to remove the DataContext binding from XAML. Please check the code changes above.
With these changes I have found out that it is not possible to use the ViewModel from ViewModelLocator because it is a "singleton" and not a new instance at each new window. Therefore, the DialogResult held the last value and if I tried to change its value back to null (as it is when the ViewModel is initialized) an exception is thrown. Do you have any clues of why this happens? It would be very good for me to use the ViewModel from ViewModelLocator, since it would keep the same strategy throughout the system.
Thank you!
I do that by implementing static XxxxInteraction classes that have methods called for example NewUser(); That methods opens the Dialogs and do some work. In my ViewModel I call the XxxxInteraction classes via commands.
The efforts of that way of implementing is, that you can easely modify the methods in the static Interaction classes for using UnitTests.
public static class UserInteractions
{
public static User NewUser()
{
var userDialog = new NewUserDialog();
If(userDialog.ShowDialog() != true) return null;
var user = new User();
user.Name = userDialog.Name;
user.Age = userDialog.Age;
return user;
}
}
public class MyViewModel
{
...
public void NewUserCommandExecute()
{
var newUser = UserInteractions.NewUser();
if(newUser == null) return;
//Do some with new created user
}
}
NewUserDialog is a normal Window that is bound to a ViewModel too.
I think this is a good way of implementing dialogs for the mvvm pattern.
i've done this a while ago, i use a dialog service and call this service in my viewmodel. take a look.
EDIT: btw, thats all you have to do in your viewmodel
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

UserControl Parent Left

So I have a user control within a window. I need to be able (from user control) to retrieve the parent window left and top (in order to locate a new popup I'm opening from the child). I'm trying to do this by referencing the UserControl .Parent property but doesn't seem to work.
Any idea? Thanks!
Are you using MVVM? Are you concerned about writing code in the code behind? .Net 3.5 or 4.0?
From the UserControl Code behind you could use:
Window parentWindow = Window.GetWindow(userControlReference);
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
Loaded += new RoutedEventHandler(UserControl1_Loaded);
//Window parrentWindow = Window.GetWindow(this);//don't add here the value will be null
}
void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
Window parrentWindow = Window.GetWindow(this);
}
}

WPF WindowStartupLocation="CenterOwner" not really center, and pops all over, why?

Well this question and this question are similar but no answers that work. In fact I was hoping WindowStartupLocation=CenterOwner would work...it doesn't. It seems to center the new window in the center of a grid column, not the center of the main window. So I'm assuming it thinks that is the parent. Second when I close the dialog and open it again it is not centered but moved down and right from the previous position. And if I move the main window to a second monitor the popup still opens on the default monitor. Are these properties wrong or am I just thinking it should work in a different way. I suppose I could calculate the Top and Left properties manually. I just want the popup to be centered in the main window no matter where it is.
Probably because you didn't set the owner:
this.Owner = App.MainWindow; // for example
That's how I do it and it centers the window perfectly all the time.
To extend on what Will Eddins commented, you could create an overload method for ShowDialog() or Show() in your Window:
public void ShowDialog(Window owner)
{
this.Owner = owner;
this.ShowDialog();
}
public void Show(Window owner)
{
this.Owner = owner;
this.Show();
}
Or overload a constructor:
public MyWindow(Window owner)
: this()
{
this.Owner = owner;
}
If you create an extention for this, you could reuse this fine idea:
/// <summary>
/// Opens a window modally, with an owner
/// </summary>
/// <param name="window">The window to open</param>
/// <param name="opener">The owner of the window getting opened</param>
/// <returns>window.ShowDialog()</returns>
public static bool? ShowDialog(this Window window, Window opener)
{
window.Owner = opener;
return window.ShowDialog();
}
In addition, we can use:
this.Owner = App.Current.MainWindow;
Or Application instead of App.
And place it in a child window constructor:
public partial class ChildWindow : Window
{
public ChildWindow()
{
InitializeComponent();
DataContext = new ChildWindowViewModel();
this.Owner = App.Current.MainWindow;
}
}
I had the same problem...but it was mostly due to the fact that, when i wanted to get rid of the child window, I used hide() instead of close() ... so when you reopen it, because it was hidden and not closed, when the parent window is moved, it still opens at it's startup location...
So when close the child window instead of hiding it for example when finished working with it.
Something else that can cause this is setting DataContext after InitializeComponent() is called.
If you have code-behind like this:
public CustomWindow(CustomViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
Change it to:
public CustomWindow(CustomViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
}

How can I access one window's control (richtextbox) from another window in wpf?

I'm sure this is something very simple but I can't figure it out. I've searched here and on msdn and have been unable to find the answer. I need to be able to set the richtextboxes selection via richtextbox.Selection.Select(TextPointer1, Textpointer2).
Application.Current contains a collection of all windows in you application, you can get the other window with a query such as
var window2 = Application.Current.Windows
.Cast<Window>()
.FirstOrDefault(window => window is Window2) as Window2;
and then you can reference the control from your code, as in
var richText = window2.MyRichTextBox
Application.Current.Windows.OfType(Of MainWindow).First
You should be able to access controls on Window1 from Window2 code behind, if that's what you want. Generated fields are internal by default.
All you need is to name the control on Window1, like this:
<RichTextBox x:Name="richtextbox" ... />
In Window2 code behind:
var window = new Window1(); // or use the existing instance of Window1
window.richtextbox.Selection.Select(TextPointer1, Textpointer2);
A better option would be to encapsulate select operation in a method in code behind of Window1, to avoid giving away internal. Then you would have:
// Window1.cs
public void Select(int param1, int param2)
{
richtextbox.Selection.Select(param1, param2);
}
// Window2.cs
var window = new Window1(); // or use the existing instance of Window1
window.Select(TextPointer1, Textpointer2);
You cant access the texbox from another window as it is private to that window you can however work around this by exposing the RichTextBox as a public property on your window (hack)
public RichTextBox RichTextBox {
get{
//the RichTextBox would have a property x:Name="richTextbox" in the xaml
return richTextBox;
}
}

Resources