Trying to make tables in mono develop - mono

I just started using monodevelop and I am having a hard time to design the forms. I have understood the concept of widgets inside containes. I am trying to get a Table which is inside a scroll view. The Table should have 3 columns and I should be able to set the column names.
I have looked at some documentation by mono develop which shows how to use the Node View to do the same but I haven't been able to figure out why is it coming in a new window and how to get it on my first screen itself.Also is there any way I can drag and drop to the form to make my columns and table header? I have attached the code :
using System;
using Gtk;
namespace ImageCompressionTool
{
class MainClass
{
public static void Main (string[] args)
{
Application.Init ();
MainWindow win = new MainWindow ();
win.Show ();
Application.Run ();
Gtk.Application.Init ();
NodeViewExample win1 = new NodeViewExample ();
win1.Show ();
Gtk.Application.Run ();
}
}
public class MyTreeNode : Gtk.TreeNode {
string song_title;
public MyTreeNode (string artist, string song_title)
{
Artist = artist;
this.song_title = song_title;
}
[Gtk.TreeNodeValue (Column=0)]
public string Artist;
[Gtk.TreeNodeValue (Column=1)]
public string SongTitle {get { return song_title; } }
}
public class NodeViewExample : Gtk.Window {
Gtk.NodeStore store;
Gtk.NodeStore Store {
get {
if (store == null) {
store = new Gtk.NodeStore (typeof (MyTreeNode));
store.AddNode (new MyTreeNode ("The Beatles", "Yesterday"));
store.AddNode (new MyTreeNode ("Peter Gabriel", "In Your Eyes"));
store.AddNode (new MyTreeNode ("Rush", "Fly By Night"));
}
return store;
}
}
public NodeViewExample () : base ("NodeView")
{
SetSizeRequest (200,150);
// Create our TreeView and add it as our child widget
Gtk.NodeView view = new Gtk.NodeView (Store);
Add (view);
// Create a column with title Artist and bind its renderer to model column 0
view.AppendColumn ("Artist", new Gtk.CellRendererText (), "text", 0);
// Create a column with title 'Song Title' and bind its renderer to model column 1
view.AppendColumn ("Song Title", new Gtk.CellRendererText (), "text", 1);
view.ShowAll ();
}
}
}
Kindly help me out. Thanks!

If you only want your NodeView window to be displayed then remove the code that is showing the MainWindow. Your Main method is currently showing two windows. If you just want it to show your NodeViewExample window then it should look like:
public static void Main (string[] args)
{
Gtk.Application.Init ();
NodeViewExample win1 = new NodeViewExample ();
win1.Show ();
Gtk.Application.Run ();
}
You cannot drag and drop columns into a Gtk.NodeView or Gtk.TreeView into the designer.

Related

Revit external command with progress bar and 'Cancel' button

I am developing a new external command for Revit. it needs a progress bar + a button to cancel its execution in any moment.
In order to get it, I haver implemented a external event.
Implementing an external event handler with de code to be executed by the command.
public class GestorDeEventoExterno : IExternalEventHandler
{
public bool CancellationRequested { get; set; }
private VentanaDeProgreso progressAndcancelWindow;
private EventWaitHandle eventWait;
public void Execute(UIApplication aplicacionDeLaIU)
{
using (this.eventWait = new AutoResetEvent(false))
{
// New thread for the progress bar.
Thread progressBarThread = new Thread(new ThreadStart(() =>
{
// Populating the progress bar window.
this.progressAndcancelWindow = new VentanaDeProgreso(this);
progressAndcancelWindow.Show();
// Chenge the state of the wait event.
this.eventWait.Set();
Dispatcher.Run();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.IsBackground = true;
progressBarThread.Start();
this.eventWait.WaitOne();
}
// Get the current revit document.
Document documentoActivo = aplicacionDeLaIU.ActiveUIDocument.Document;
// Code to simulate the revit command operation.
for (int i = 0;
i <= 100;
i++)
{
// Code to be executed if a cancellation has been requested.
if (this.CancellationRequested)
{
TaskDialog.Show("Test", "Cancel");
this.progressAndcancelWindow.Dispatcher.Invoke(new Action(this.progressAndcancelWindow.Close));
return;
}
this.progressAndcancelWindow.ActualizarProgreso($"loop number: {i}", i, 100);
Thread.Sleep(100);
}
this.progressAndcancelWindow.Dispatcher.Invoke(new Action(this.progressAndcancelWindow.Close));
TaskDialog.Show("Test", "END");
}
public string GetName()
{
return "test";
}
}
Implementing an external command to register the external event and populate the main window
public class Class1 : IExternalCommand
{
public Result Execute(
ExternalCommandData externalCommandData,
ref string message,
ElementSet elements)
{
// Registering the external event.
GestorDeEventoExterno externalEventHandler = new GestorDeEventoExterno();
ExternalEvent externalEvent = ExternalEvent.Create(externalEventHandler);
// Populating the main window.
VentanaPrincipal mainWindow = new VentanaPrincipal(
externalEvent);
mainWindow.Show();
return Result.Succeeded;
}
}
Finally, the code behind of the progress bar window
public partial class VentanaDeProgreso : Window
{
private GestorDeEventoExterno externalEventHandler;
public void ActualizarProgreso(
string texto,
int valorActual,
int valortotal = 100)
{
this.Dispatcher.Invoke(
new Action<string, int, int>(
delegate (string txt, int vActual, int vTotal)
{
this.IndicadorDeProgreso.Value = valorActual;
this.IndicadorDeProgreso.Maximum = vTotal;
this.Texto.Text = txt;
}),
System.Windows.Threading.DispatcherPriority.Background,
texto,
valorActual,
valortotal);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// AsignaciĆ³n de valor verdadero a la propiedad de cancelaciĆ³n solicitada del evento externo.
this.externalEventHandler.CancellationRequested = true;
}
public VentanaDeProgreso(GestorDeEventoExterno externalEventHandler)
{
InitializeComponent();
this.externalEventHandler= externalEventHandler;
}
}
AS you can see, the progress window has the external event handler as a property and the cancel button click event sets the property 'CancellationRequested'.
My question is: How can I improve it?
You do not need an external event to cancel your command.
You only need an external event to cancel submit a request to execute Revit API functionality from some context in which it is not available.
Your cancellation requires no Revit API functionality, just your own stuff, hence no external event.
Therefore, you can restructure the whole solution much more simply. Kiss!

Is it somehow possible to have two master views and one detail view?

If I have for example one master view on the left and one in the middle, each showing oder Java Beans/POJOs, can I use a shared detail view that somehow listens to the active beans of each view and then displays the currently selected one in more detail? A one to one relation is quite easy to manage by using your Context library.
#ViewDocking(areaId ="left", position=1, displayName="Profiles", menuEntry = #WindowMenuEntry(path = "", position=0), accelerator="Shortcut+1")
public class ProfileListView extends BorderPane implements LocalContextProvider {
private final SimpleContextContent content = new SimpleContextContent();
private final SimpleContext context = new SimpleContext(content);
#FXML
private ListView<Profile> listview;
public ProfileListView() {
load();
// add some profiles
listview.getItems().add(new Profile("Profile1"));
listview.getItems().add(new Profile("Profile2"));
listview.getItems().add(new Profile("Profile3"));
// setup selection listener
listview.getSelectionModel().selectedItemProperty().addListener((value, oldProfile, newProfile) -> {
// set active profile and remove old one
content.remove(oldProfile);
content.add(newProfile);
});
// setup double click listener
configureClickListener();
}
private Profile getSelectedProfile() {
return listview.getSelectionModel().getSelectedItem();
}
private void configureClickListener() {
listview.setOnMouseClicked(event -> {
// check if it was a double click
if(event.getClickCount() == 2) {
System.out.println(getSelectedProfile());
// inject into editor pane
// calls the procedure to create a tab in the center area...
}
});
}
private void load() {
FXMLLoaders.loadRoot(this);
}
#Override
public Context getLocalContext() {
return context;
}
}
This is one master view holding a list view of items.
The other one would be the same, docking to the right as another tab and holding POJOs of type 'Action'.
The detail view is here:
#ViewDocking(areaId = "right", displayName = "Properties", accelerator = "Shortcut+2", menuEntry = #WindowMenuEntry(path = "", position = 0), position = 1)
public class ProfilePropertiesView extends BorderPane implements LocalContextProvider, ActiveContextSensitive {
private Context activeContext;
private SimpleContextContent content = new SimpleContextContent();
private SimpleContext context = new SimpleContext(content);
private Profile profile;
private IWindowService service = new NullWindowService();
#FXML
private PropertySheet propertysheet;
public ProfilePropertiesView() {
load();
// retrieve framework service, TODO: use tracker
BundleContext ctx = FrameworkUtil.getBundle(getClass()).getBundleContext();
service = ctx.getService(ctx.getServiceReference(IWindowService.class));
// initialize callback
service.addCallback(title -> {
System.out.println("callback called " + title);
// update the property sheet ui by re-creating the items list
// updateUI();
// we can safely return null
return null;
});
// configure editor factory so the user is able to use a combobox
propertysheet.setPropertyEditorFactory(new CustomPropertyEditorFactory(service));
}
private void load() {
FXMLLoaders.loadRoot(this);
}
#Override
public Context getLocalContext() {
return context;
}
private void contextChanged() {
// find profile information
Profile found = activeContext.find(Profile.class);
// if the found profile is null, ignore it
if (found != null) {
// reset if profile is valid
if (profile != null) {
reset();
}
// create reference and register
profile = found;
register();
}
}
private void register() {
// retrieve observablelist of bean properties if some profile is selected
if(profile != null) {
ObservableList<Item> items = createDetailedList(profile);
propertysheet.getItems().setAll(items);
}
}
private void updateUI() {
// clear property elements and re-create them
reset();
// re-create items
ObservableList<Item> items = createDetailedList(profile);
propertysheet.getItems().addAll(items);
}
private ObservableList<Item> createDetailedList(Object bean) {
ObservableList<Item> list = FXCollections.observableArrayList();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(), Object.class);
Arrays.stream(beanInfo.getPropertyDescriptors()).map(pd -> new DetailedBeanProperty(bean, pd)).forEach(list::add);
} catch (IntrospectionException e) {
e.printStackTrace();
}
return list;
}
private void reset() {
propertysheet.getItems().clear();
}
#Override
public void setActiveContext(Context activeContext) {
this.activeContext = activeContext;
this.activeContext.addContextListener(Profile.class, event -> contextChanged());
// trigger change
contextChanged();
}
}
The current ProfilePropertiesView is just configured to display the properties of the selected profile. I want it to be able to display the current information of the last selected POJO in the UI. That means that if the user selected a Profile from the ListView, that profile should be displayed in the properties view. If he selected an Action from the Table (which is displayed in the center), the properties of the Action should be displayed.
Do I just need to register a new ContextListener for the Action.class
POJO and then call a method to populate the PropertiesView? I was
unsure if this is the right solution...
Yes, just add another ContextListener to the activeContext for every POJO type you want to observe.
Also note that in the constructor of views it's better to use a ServiceTracker instead of looking for the service via BundleContext as the service might not be available yet, depending on the order the bundles are loaded.
You can find a sample which uses a ServiceTracker here: https://stackoverflow.com/a/35974498/506855

How to display a Labels 'Error on ErrorProvider1'

Goal
I want to display the text that I put in the Label's "Error on ErrorProvider1" attribute whenever I get an error. See the following label's attributes below.
I try to display the text in the red rectangle into my ErrorProvider1 SetError(control, value) function.
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, ErrorProvider1.GetError(lblErr))
Else
ErrorProvider1.SetError(lblErr, "")
End If
How can I retrieve the 'Error on ErrorProvider1' text from the lblErr to display it in the ErrorProvider1 SetError value?
The ErrorProvider component is very awkward to use effectively. It is fixable however, I'll give an example in C# that extends the component with some new capabilities:
ShowError(Control ctl, bool enable) displays the text that you entered at design-time when the enable argument is true. The easier-to-use version of SetError().
HasErrors returns true if the any active warning icons are displayed. Handy in your OK button's Click event handler.
FocusError() sets the focus to the first control that has a warning icon, if any. It returns false if no warnings are remaining.
SetError() is a replacement of ErrorProvider.SetError(). You only need it if you add any controls after the form's Load event fired or if you need to modify the warning text.
Add a new class to your project and paste the code shown below. Compile. Drop it from the top of the toolbox onto the form. The design-time behavior is identical. Modestly tested.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.ComponentModel.Design;
class MyErrorProvider : ErrorProvider {
public void ShowError(Control ctl, bool enable) {
// Easy to use version of SetError(), uses design-time text
if (!enable) base.SetError(ctl, "");
else {
if (errors.ContainsKey(ctl)) base.SetError(ctl, errors[ctl]);
else base.SetError(ctl, "No error text available");
}
}
public bool HasErrors {
// True if any errors are present
get {
foreach (var err in errors)
if (!string.IsNullOrEmpty(base.GetError(err.Key))) return true;
return false;
}
}
public bool FocusError() {
// Set the focus to the first control with an active error
foreach (var err in errors) {
if (!string.IsNullOrEmpty(base.GetError(err.Key))) {
err.Key.Focus();
return true;
}
}
return false;
}
public new void SetError(Control ctl, string text) {
// Use this only to add/modify error text after the form's Load event
if (!string.IsNullOrEmpty(text)) {
if (errors.ContainsKey(ctl)) errors[ctl] = text;
else errors.Add(ctl, text);
}
base.SetError(ctl, text);
}
private void initialize(object sender, EventArgs e) {
// Preserve error text
copyErrors(((Form)sender).Controls);
}
private void copyErrors(Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
var text = this.GetError(ctl);
if (!string.IsNullOrEmpty(text)) {
errors.Add(ctl, text);
base.SetError(ctl, "");
}
copyErrors(ctl.Controls);
}
}
private Dictionary<Control, string> errors = new Dictionary<Control, string>();
// Plumbing to hook the form's Load event
[Browsable(false)]
public new ContainerControl ContainerControl {
get { return base.ContainerControl; }
set {
if (base.ContainerControl == null) {
var form = value.FindForm();
if (form != null) form.Load += initialize;
}
base.ContainerControl = value;
}
}
public override ISite Site {
set {
// Runs at design time, ensures designer initializes ContainerControl
base.Site = value;
if (value == null) return;
IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service == null) return;
IComponent rootComponent = service.RootComponent;
this.ContainerControl = rootComponent as ContainerControl;
}
}
}
Your issue is that you are replacing the error message when nothing is wrong. As noted in your comment below, you are storing the localized error message in the label's Tag, so you can do the following:
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, lblErr.Tag)
Else
ErrorProvider1.SetError(lblErr, "")
End If
You were correct to use ErrorProvider1.GetError(Control) to get the value. It's just that you're more than likely replacing it with an empty string before you were retrieving it.

Show a Dialog() or MessageDialog() from the MainWindow.cs?

I'm trying to display a Dialog() or MessageDialog() from my MainWindow.cs (standard MainWindow.cs file that is created from the C# GTK# 2.0 Project template).
I'm getting a rather nasty error when using the below code:
public partial class MainWindow : Gtk.Window
{
public MainWindow () : base(Gtk.WindowType.Toplevel)
{
Build();
}
public void CreateAlert(string message)
{
Console.WriteLine(string.Format("CreateAlert() - message: {0}", message));
Dialog dialog = new Dialog("Error", this, Gtk.DialogFlags.DestroyWithParent);
dialog.Modal = true;
dialog.AddButton ("OK", ResponseType.Close);
dialog.Response += on_dialog_response;
dialog.Run ();
dialog.Destroy ();
}
}
I believe the cause of this error is the second param in the Dialog() constructor - "this". My question is... how does one satisfy the second param of "Window win" when inside the MainWindow.cs?
Thanks in advance.

WebView title is sometimes null when it shouldn't be

I'm writing a simple browser in Vala and WebKitGTK+.
One of the things I need to do is set the window's title to that of the webpage title, so I monitor title changes with web_view.notify["title"].connect. However, sometimes the value of title is null, when it obviously shouldn't be.
Some examples I remember:
Search anything in Google. Going to the next results page sets the title to null.
Click on an anchor link (e.g. http://example.com/page.html#section)
In any case, using the Web Inspector shows that the pages do have a title set.
Is this a bug I should report? Or maybe I'm doing something wrong? Here's the code I'm using:
//valac --thread --pkg webkitgtk-3.0 --pkg gtk+-3.0 --vapidir=./ test.vala
//The vapidir folder should have webkitgtk-3.0.vapi and webkitgtk-3.0.deps
using WebKit;
using Gtk;
public class Test : Gtk.Window {
public WebView webview;
public Test () {
this.title = "Test";
this.window_position = Gtk.WindowPosition.CENTER;
this.set_default_size (800, 600);
this.hide_titlebar_when_maximized = false;
this.destroy.connect (() => {
Gtk.main_quit ();
});
var scroll = new ScrolledWindow (null, null);
this.webview = new WebView ();
this.add (scroll);
scroll.add (webview);
webview.settings.enable_developer_extras = true;
webview.notify["title"].connect ((sender, property) => {
if (webview.title != null) {
this.title = webview.title;
stdout.printf (webview.title + "\n");
} else {
stdout.printf ("(null)\n");
}
});
webview.web_inspector.inspect_web_view.connect ((p0) => {
var w = new Window ();
w.window_position = WindowPosition.CENTER;
w.title = "Inspector";
w.set_default_size (800,600);
WebView view = new WebView ();
unowned WebView view2 = view;
w.add (view2);
w.show_all ();
return view2;
});
}
public static int main (string[] args) {
Gtk.init (ref args);
Test app = new Test ();
app.webview.load_uri ("http://google.com");
app.show_all ();
Gtk.main ();
return 0;
}
}
There is a signal called title_changed for that purpose and you should always use the signals provided by webkitgtk instead of the GLib notify feature.
(GLib notify is emitted each time a value changes, even if the change is just for the purpose of clearing the old value, such as in your case.)