C# VSTO - dynamically set RibbonControl - c#

I have a customized Ribbon in Word. The Ribbon has one comboBox: comboBox_recentConditions which was defined using the Designer - so it's initalized and is empty at load. Now, I would like to dynamically set this comboBox each time the Application_WindowActivate event is fired.
Each Word document has its own instance of class called RibbonControls:
class RibbonControls
{
private RibbonComboBox recentConditionComboBox;
public RibbonControls()
{
this.recentConditionComboBox = new RibbonComboBox();
}
public RibbonComboBox RecentConditionComboBox
{
get
{
return recentConditionComboBox;
}
set
{
recentConditionComboBox = value;
}
}
}
Now in Application_WindowActivate event i do the following:
static void Application_WindowActivate(Document doc, Window Wn)
{
Globals.Ribbons.SourceRibbon.comboBox_recentConditions = WordGate.docRibbonControls.RecentConditionComboBox;
}
The problem is that the Ribbon comboBox control doesn't changes, it's always empty, even after Application_WindowActivate is called.
I tested at run-time to see if each document indeed has its own comboBox with its items - which seems to work.
What am I missing?
To clear my question:
Let's say I have 3 items in the comboBox.Items. When clicking on it I see nothing, but if I add this:
MessageBox.Show(Globals.Ribbons.SourceRibbon.comboBox_recentConditions.Items.Count.ToString());
at the end of Application_WindowActivate it will print the number 3.
Thanks.

You cant do it that way. Please have a read on Ribbon callbacks
You have to use the getItemLabel event which will add items to combo box. If you want to load the combobox during WindowActivate then call Ribbon.Invalidate or Ribbon.InvalidateControl which will call all relevant ribbon callbacks.

Related

How To Set TabPage on start?

I have a TabControl with three TabPages. The initial start of the app opens always the first TabPage on the left side. For me it's necessary to set the starting TabPage (for example the second one).
Of course, I know about possibilities to change the tab on start like these:
tabControl.SelectedTab = tabPage;
tabControl.SelectTab(tabPage);
...
But this code would also activate additional events to fire like TabControl.Selecting, TabControl.Deselecting, TabControl.SelectedIndexChanged etc. — I would really like to prevent this in advance.
What I am looking for is some kind of property in the TabControl like "StartingTabPageIndex" - setting it to 1 would open the second TabPage on start without invoking any unnecessary events.
Another option. Go into the Form Designer, change the SelectedIndex property from 0 to 1:
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Controls.Add(this.tabPage3);
this.tabControl1.Location = new System.Drawing.Point(223, 21);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 1; // <-- This Line
this.tabControl1.Size = new System.Drawing.Size(300, 143);
this.tabControl1.TabIndex = 3;
The event handlers aren't connected yet, and making any modifications to the TabControl in the designer doesn't seem to affect that property. It seems safe to change it this way.
You should remove the binding with the event handlers from the designer and add them after you have set the initial tabpage
After removing them in the designer (this doesn't delete the event handler code) rebind the event handler in the form load event after setting the required tabpage
tabControl.SelectedTab = tabPage;
tabControl.Selected += tabControl_Selected;
.... and so on for the other events to handle....
Update
I just made a simple test, and SelectedTab does not work because it expects the handle to be created on set.
However this seems to work:
public class MyTabControl : TabControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Browsable(true)]
public new int SelectedIndex
{
get { return base.SelectedIndex; }
set { base.SelectedIndex = value; }
}
}
You'll now be able to see SelectedIndex in the designer and can set it. It won't change the visible tab in the designer, but it will store the "initial tab index" (zero-based).
It does change SelectedIndex, but it does not call the events since events are assigned last in the designer's serialization, so they are never assigned before the change.
Old
One option would be having SelectedTab serialized. You'd only need to derive your own custom TabControl from TabControl and have something like this:
public class MyTabControl : TabControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public new TabPage SelectedTab {
get { return base.SelectedTab; }
set { base.SelectedTab = value; }
}
}
That way you'll get your designer selected SelectedTab as initial.
I haven't tested this, but theory says it should work :-)

ObjectListView editing doesn't work

I'm trying to create a simple listbox with ObjectListView (WinForm, C#). The goal is to have a single value (a double) and a check box.
I want to be able to edit the double value by Single Click, so here are the relevant lines of code from my MyWindow.Designer.cs file (i've left out the default values for efficiency):
this.olvDepths = new BrightIdeasSoftware.ObjectListView();
this.olvColumn1 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
...
this.olvDepths.CellEditActivation = BrightIdeasSoftware.ObjectListView.CellEditActivateMode.SingleClick;
this.olvDepths.CheckBoxes = true;
this.olvDepths.CheckedAspectName = "IsDefault";
this.olvDepths.FullRowSelect = true;
//
// olvColumn1
//
this.olvColumn1.AspectName = "Depth";
this.olvColumn1.Text = "";
this.olvColumn1.IsEditable = true;
I then create a list of my class (ShieldingEntry) and use the olvDepths.SetObjects() with the list. My ShieldingEntry class looks like this:
public class ShieldingEntry
{
public double Depth { get; set; }
public bool IsDefault { get; set; }
}
However, when I click the field, it doesn't go into edit mode. I've also tried the DoubleClick, SingleClickAlways, and F2Only modes and they don't work either.
The Checkbox works fine.
************** I have additional information *********************
I've pulled and build the ObjectListView source, so I could step through it.
I put a breakpoint in the OLV StartCellEdit method and it gets called and appears to setup and select the control appropriately. It just never appears...
As I noted in the comments on the answer below, I've got this control on a tabbed dialog, and if I switch to another tab, then back, the control works fine.
What am I missing?
I've used ObjectListView before, and here is what I had to do:
Handle the CellEditStarting event. This event is raised when the cell goes into edit mode. Since OLV doesn't really have built-in editors, you have to make your own. Then handle the CellEditFinishing event to validate the data before putting it back into your model.
So first, handling the CellEditStarting event:
private void objlv_CellEditStarting(object sender, CellEditEventArgs e)
{
//e.Column.AspectName gives the model column name of the editing column
if (e.Column.AspectName == "DoubleValue")
{
NumericUpDown nud = new NumericUpDown();
nud.MinValue = 0.0;
nud.MaxValue = 1000.0;
nud.Value = (double)e.Value;
e.Control = nud;
}
}
This creates your editing control. If you want to make sure the size is right, you can set the size of the control (in this case a NumericUpDown) to the cell bounds using e.CellBounds from the event object.
This will show the editor when you click in the cell. Then you can handle the editor finished event to validate the data:
private void objlv_CellEditFinishing(object sender, CellEditEventArgs e)
{
if (e.Column.AspectName == "DoubleValue")
{
//Here you can verify data, if the data is wrong, call
if ((double)e.NewValue > 10000.0)
e.Cancel = true;
}
}
I don't think handling it is required, but its good practice to validate data from the user.
The editing control in the CellEditStarting event can be any control, even a user defined one. I've used a lot of user defined controls (like textboxes with browse buttons) in the cell editor.
[Edit]
I uploaded an example here dropbox link that seems to work. Might not be in the exact view as needed, but seems to do the job.
For anyone else with this problem. I had it specifically when trying to edit a 'null' value in a decimal? on the OLV on a tab page. Solution for me was to set UseCustomSelectionColors to 'False'. I didn't look elsewhere to see if it was reported as a bug. Seems like a bug.

Usercontrol contains a combobox, but the Items set at design time are not in the run time

I have a user control that contains a textbox and a combobox. I have exposed the combobox's Item property to clients of the user control, thus:
public System.Windows.Forms.ComboBox.ObjectCollection Item
{
get { return baseComboBox.Items; }
}
I added the user control to a windows form, and set the Items list using the property values editor in the form designer. I then ran the application, and the combobox's drop down list was empty. To confirm that the items added at design time were not in the list, I added the following two lines of code to the client form:
textBox1.Text = userControl1.Items.Count.ToString();
userControl1.Items.Add("Test item");
When I re-ran the application the test box showed a count of 0 (zero), and the drop-down list of the user control contained only "Test item".
Thinking that maybe the instance of the user control being referenced at design time is a different instance from that being referenced at run time, I set the user control's BackColor property at design time. When I re-ran the app, the user control's BackColor was what I had set it to in the designer.
Any ideas on why the design time setting of the Items does not carry over into the run time?
You need an attribute:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ComboBox.ObjectCollection Item {
get { return baseComboBox.Items; }
}
Maybe you need the setter defined also, something like:
public System.Windows.Forms.ComboBox.ObjectCollection Item
{
get { return baseComboBox.Items; }
set { baseComboBox.Items = value; }
}
Or, maybe it's because you are exposing Item but setting Items
Defining Set as a simple baseComboBox.Items = value; is impossible because baseComboBox.Items is defined as ReadOnly.
The problem is also the lack of a defined editor for such a collection.
Therefore, you should add the editor definition and instead of trying to replace the collection as one object - use AddRange:
[Editor("System.Windows.Forms.Design.StringCollectionEditor, " +
"System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ComboBox.ObjectCollection Items
{
get
{
return baseComboBox.Items;
}
set
{
baseComboBox.Items.Clear();
baseComboBox.Items.AddRange(value.Cast<object>().ToArray());
}
}

WPF C# How to avoid event fire whle importing?

I have a WPF app with a Window and different UserControls are shown in it one by one no button clicks.
I import data from a file and all data is stored in a common object "ImportExportData". All UserControls are bind to respective Property (as custom objects like Data1, Data2...) of ImportExportData class.
In my USerControl I have combobox's for NumberZones proeprty those SelectionChanged event is handled respectively. In the SelectionChanged event of this combobox, based on the number selected that many rows are added to an ObservableCollection of Data2 property.
While importing data and setting the imported object (Data2) as the DataContext of USerControl2, it sets the NumberZones property value to the respective combobox and SelectionChanged event is fired as it should. At this time, the object already contains reqd rows in ObservableCollection and this event should not add it.
PArent window has a flag "importedData" that tells me that the object is imported. But I can't make that false once UserContrl2 is loaded, as their are their UC that will follow UC2. In UC2 I can create another flag "importing" and make it false once all UI is loaded. Thru which UC event can I know that UI is loaded and thus make "importing" as false ??
I am wondering how do I avoid from firing the SelectionChanged event when the imported object is populating the UI components. Which event of the UserControl will help me in this case maybe to keep a flag in USerControl2.
Any idea, suggestions please.
It is very hard to understand all of your question, so bear with me... I'll address each point that I understand.
Thru which UC event can I know that UI is loaded and thus make "importing" as false ??
Take a look at the FrameworkElement.Loaded Event page at MSDN.
I am wondering how do I avoid from firing the SelectionChanged event when the imported object is populating the UI components.
There are two way of achieving this goal... The first way does not stop the event from firing, but instead ignores it when data is being imported. basically involves temporarily unsubscribing from the SelectionChanged event and then re-subscribing to it. If I understand you correctly, you have a bool property in your parent Window and SelectionChanged handlers in your UserControls... first, you can add a bool property to each of your UserControls:
public bool CanChangeSelection { get; set; }
Now, in your parent Window (assuming that you have references to your controls) you can update your property:
private bool isImporting = false;
public bool IsImporting
{
get { return isImporting; }
set
{
isImporting = value;
UserControl1.CanChangeSelection = isImporting;
UserControl2.CanChangeSelection = isImporting;
...
UserControlN.CanChangeSelection = isImporting;
}
}
Then finally, in your control SelectionChanged handlers:
private void SelectionChangedHandler(object sender, RoutedEventArgs e)
{
if (CanChangeSelection)
{
// do your stuff in here
}
}
The second way basically involves temporarily unsubscribing from the SelectionChanged event and then re-subscribing to it. For this option, we need to change the definition of our new bool property in each of your UserControls:
private bool canChangeSelection = false;
public bool CanChangeSelection
{
get { return canChangeSelection; }
set
{
canChangeSelection = value;
if (!canChangeSelection)
{
if (SelectionChangedHandler != null) ComboBox1.SelectionChanged -=
SelectionChangedHandler;
}
else if (SelectionChangedHandler == null) ComboBox1.SelectionChanged +=
SelectionChangedHandler;
}
}
I personally prefer the first method as it is more straightforward.

C# grid binding not update

I have a grid that is binded to a collection. For some reason that I do not know, now when I do some action in the grid, the grid doesn't update.
Situation : When I click a button in the grid, it increase a value that is in the same line. When I click, I can debug and see the value increment but the value doesn't change in the grid. BUT when I click the button, minimize and restore the windows, the value are updated... what do I have to do to have the value updated like it was before?
UPDATE
This is NOT SOLVED but I accepted the best answer around here.
It's not solved because it works as usuall when the data is from the database but not from the cache. Objects are serialized and threw the process the event are lost. This is why I build them back and it works for what I know because I can interact with them BUT it seem that it doesn't work for the update of the grid for an unkown reason.
In order for the binding to be bidirectional, from control to datasource and from datasource to control the datasource must implement property changing notification events, in one of the 2 possible ways:
Implement the INotifyPropertyChanged interface, and raise the event when the properties change :
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
NotifyPropertyChanged("Name");
}
}
}
Inplement a changed event for every property that must notify the controls when it changes. The event name must be in the form PropertyNameChanged :
public event EventHandler NameChanged;
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
if (NameChanged != null) NameChanged(this, EventArgs.Empty);
}
}
}
*as a note your property values are the correct ones after window maximize, because the control rereads the values from the datasource.
It sounds like you need to call DataBind in your update code.
I am using the BindingSource object between my Collection and my Grid. Usually I do not have to call anything.

Categories

Resources