How does WorldEdit handle brushes? - minecraft

I'm trying to find out how the Bukkit version of WorldEdit handles brushes. I've been looking at the source code on GitHub, but I couldn't find anythig useful. I've tried to recreate the effect, but I can only get it to work when I'm in interaction reach of the target block.
This is about as close as it gets in the source code:
} else if (action == Action.RIGHT_CLICK_AIR) {
if (we.handleRightClick(player)) {
event.setCancelled(true);
}
}
(WorldEdit/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java, line 143-147)
There are some other parts of code that get very close. I've also looked in /worldedit-core, but nothing there either.
Could someone help me here?
Edit: This is how I try to do it:
public static void onRightClick (PlayerInteractEvent event) {
if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
Location location = event.getClickedBlock().getLocation();
if (event.getItem() != null) {
if (event.getItem().getItemMeta().equals(ItemManager.wand.getItemMeta())) {
Player player = event.getPlayer();
player.getWorld().doStuff(location);
}
}
}
}
Edit #2: what I'm most curious about is: How does WE select the location to apply the brush if you are outside of interaction reach?

I needed to use BlockIterators for this. The final code looks like this:
public class BoomWandEvent implements Listener {
#EventHandler
public static void onRightClick (PlayerInteractEvent event) {
Player player = event.getPlayer();
if (event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.RIGHT_CLICK_AIR) {
if (player.getInventory().getItemInMainHand().equals(ItemManager.explosionWand)) {
Location eyePos = player.getEyeLocation();
BlockIterator raytracer = new BlockIterator(eyePos, 0.0D, player.getClientViewDistance() * 16);
while (raytracer.hasNext()) {
Location location = raytracer.next().getLocation();
if (player.getWorld().getBlockAt(location).getType() != Material.AIR && player.getWorld().getBlockAt(location).getType() != Material.CAVE_AIR && player.getWorld().getBlockAt(location).getType() != Material.VOID_AIR) {
player.getWorld().createExplosion(location, 4f);
return;
}
}
}
}
}
}
Thanks to Rogue for helping!

Related

Codename One location sometimes not working

Old question: Codename One app not provide real location
We still have problem getting current location.
Sometimes it's ok, "Localizzazione..." dialog shows, then location ok callback dispose the dialog.
Sometimes the dialog is never disposed and I don't see GPS in the top bar, which is visible when location is ok and dispose the dialog.
Slider s1 = new Slider();
Display.getInstance().callSerially(() -> {
blocco_loc_in_corso = makeDialog("Localizzazione...", s1, null, 'a');
blocco_loc_in_corso.show();
});
LocationManager locationManager = LocationManager.getLocationManager();
locationManager.setLocationListener(new LocationListener() {
#Override
public void locationUpdated(Location location) {
if(location != null) {
Display.getInstance().callSerially(() -> {
if(blocco_loc_in_corso != null) {
blocco_loc_in_corso.dispose();
}
});
paintLocation(location, true);
}
}
#Override
public void providerStateChanged(int newState) {
}
}, new LocationRequest(LocationRequest.PRIORITY_HIGH_ACCUARCY, 1000));
I have this problem for at least 6 months. We only need to block user until we have his GPS location which may can change (GPS updates callback).
Edited:
public Dialog makeDialog(String label, Component c, String buttonText, char btIcon) {
Dialog dlg_r = new Dialog();
Style dlgStyle = dlg_r.getDialogStyle();
dlgStyle.setBorder(Border.createEmpty());
dlgStyle.setBgTransparency(255);
dlgStyle.setBgColor(0xffffff);
Label title = dlg_r.getTitleComponent();
title.getUnselectedStyle().setFgColor(0xff);
title.getUnselectedStyle().setAlignment(Component.LEFT);
dlg_r.setLayout(BoxLayout.y());
Label blueLabel = new Label(label);
blueLabel.setShowEvenIfBlank(true);
blueLabel.getUnselectedStyle().setBgColor(0xff);
blueLabel.getStyle().setFgColor(0x0a0afc);
blueLabel.getStyle().setAlignment(Component.CENTER);
blueLabel.getUnselectedStyle().setPadding(1, 1, 1, 1);
blueLabel.getUnselectedStyle().setPaddingUnit(Style.UNIT_TYPE_PIXELS);
dlg_r.add(blueLabel);
dlg_r.add(c);
if (buttonText != null) {
Button dismiss = new Button(buttonText);
dismiss.getAllStyles().setBorder(Border.createEmpty());
dismiss.getAllStyles().setFgColor(0);
dismiss.getAllStyles().set3DText(true, true);
dismiss.setIcon(FontImage.createMaterial(btIcon, dismiss.getStyle()));
dismiss.addActionListener(((evt) -> {
dlg_r.dispose();
}));
dlg_r.add(dismiss);
}
return dlg_r;
}
To make sure this code is threadsafe make the following change:
public void locationUpdated(Location location) {
locationFound = true;
// ...
}
Then in the make dialog method:
dlg_r.addShowListener(e -> {
if(locationFound) {
dlg_r.dispose();
}
});
Since this event can happen in the dead time of showing the dialog transition.

How to SuggestAppend a ComboBox containing a string

Goal
I'd like to have my ComboBox items suggest and append its items when something is contained in them, not just via the StartsWith function.
My ComboBox is bound to a DataView which contains clients [CompanyName], [Address], [City] in a long concatenation.
I want my users to be able to type in the city and still find the records which matches with all of the fields above. I know this is possible with Infragistics but I don't have that package.
Search Term: "Sher"
Costco, 123 1st Avenue, Sherbrooke
Provigo, 344 Ball Street, Sherbrooke
Sherbox, 93 7th Street, Montreal
Is this possible in VB.Net or should I be searching for something else?
I did some research and found the following question:
Override Winforms ComboBox Autocomplete Suggest Rule
In that question they reffer to another question:
C# AutoComplete
Let's quote the best answer from that question
The existing AutoComplete functionality only supports searching by
prefix. There doesn't seem to be any decent way to override the
behavior.
Some people have implemented their own autocomplete functions by
overriding the OnTextChanged event. That's probably your best bet.
For example, you can add a ListBox just below the TextBox and set
its default visibility to false. Then you can use the OnTextChanged
event of the TextBox and the SelectedIndexChanged event of the
ListBox to display and select items.
This seems to work pretty well as a rudimentary example:
public Form1()
{
InitializeComponent();
acsc = new AutoCompleteStringCollection();
textBox1.AutoCompleteCustomSource = acsc;
textBox1.AutoCompleteMode = AutoCompleteMode.None;
textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
}
private void button1_Click(object sender, EventArgs e)
{
acsc.Add("[001] some kind of item");
acsc.Add("[002] some other item");
acsc.Add("[003] an orange");
acsc.Add("[004] i like pickles");
}
void textBox1_TextChanged(object sender, System.EventArgs e)
{
listBox1.Items.Clear();
if (textBox1.Text.Length == 0)
{
hideResults();
return;
}
foreach (String s in textBox1.AutoCompleteCustomSource)
{
if (s.Contains(textBox1.Text))
{
Console.WriteLine("Found text in: " + s);
listBox1.Items.Add(s);
listBox1.Visible = true;
}
}
}
void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
textBox1.Text = listBox1.Items[listBox1.SelectedIndex].ToString();
hideResults();
}
void listBox1_LostFocus(object sender, System.EventArgs e)
{
hideResults();
}
void hideResults()
{
listBox1.Visible = false;
}
There's a lot more you could do without too much effort: append text
to the text box, capture additional keyboard commands, and so forth.
Improved the technique demonstrated by BenD in his answer so as to have the mechanism handle a bit more elegantly certain cornercases:
public sealed class CCComboboxAutocomplete : ComboBox
{
public CCComboboxAutocomplete()
{
AutoCompleteMode = AutoCompleteMode.Suggest; //crucial otherwise exceptions occur when the user types in text which is not found in the autocompletion list
}
protected override void OnTextChanged(EventArgs e)
{
try
{
if (DesignMode || !string.IsNullOrEmpty(Text) || !Visible) return;
ResetCompletionList();
}
finally
{
base.OnTextChanged(e);
}
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
try
{
if (DesignMode) return;
if (e.KeyChar == '\r' || e.KeyChar == '\n')
{
e.Handled = true;
if (SelectedIndex == -1 && Items.Count > 0 && Items[0].ToString().ToLowerInvariant().StartsWith(Text.ToLowerInvariant()))
{
Text = Items[0].ToString();
}
DroppedDown = false;
return; //0
}
BeginInvoke(new Action(ReevaluateCompletionList)); //1
}
finally
{
base.OnKeyPress(e);
}
}
//0 Guardclose when detecting any enter keypresses to avoid a glitch which was selecting an item by means of down arrow key followed by enter to wipe out the text within
//1 Its crucial that we use begininvoke because we need the changes to sink into the textfield Omitting begininvoke would cause the searchterm to lag behind by one character That is the last character that got typed in
private void ResetCompletionList()
{
_previousSearchterm = null;
try
{
SuspendLayout();
var originalList = (object[])Tag;
if (originalList == null)
{
Tag = originalList = Items.Cast<object>().ToArray();
}
if (Items.Count == originalList.Length) return;
while (Items.Count > 0)
{
Items.RemoveAt(0);
}
Items.AddRange(originalList);
}
finally
{
ResumeLayout(performLayout: true);
}
}
private string _previousSearchterm;
private void ReevaluateCompletionList()
{
var currentSearchterm = Text.ToLowerInvariant();
if (currentSearchterm == _previousSearchterm) return; //optimization
_previousSearchterm = currentSearchterm;
try
{
SuspendLayout();
var originalList = (object[])Tag;
if (originalList == null)
{
Tag = originalList = Items.Cast<object>().ToArray(); //0
}
var newList = (object[])null;
if (string.IsNullOrEmpty(currentSearchterm))
{
if (Items.Count == originalList.Length) return;
newList = originalList;
}
else
{
newList = originalList.Where(x => x.ToString().ToLowerInvariant().Contains(currentSearchterm)).ToArray();
}
try
{
while (Items.Count > 0) //1
{
Items.RemoveAt(0);
}
}
catch
{
try
{
Items.Clear();
}
catch
{
}
}
Items.AddRange(newList.ToArray()); //2
}
finally
{
if (currentSearchterm.Length >= 2 && !DroppedDown)
{
DroppedDown = true; //3
Cursor.Current = Cursors.Default; //4
Text = currentSearchterm; //5
Select(currentSearchterm.Length, 0);
}
ResumeLayout(performLayout: true);
}
}
//0 backup original list
//1 clear list by loop through it otherwise the cursor would move to the beginning of the textbox
//2 reset list
//3 if the current searchterm is empty we leave the dropdown list to whatever state it already had
//4 workaround for the fact the cursor disappears due to droppeddown=true This is a known bu.g plaguing combobox which microsoft denies to fix for years now
//5 Another workaround for a glitch which causes all text to be selected when there is a matching entry which starts with the exact text being typed in
}
Sorry for another answer in C# but I have a more improved answer based on xDisruptor's code.
Using kinda behavior (decorator).
You don't have to subclass ComboBox and change all existing combos in the designed.
Be careful when using Datasource instead of Items collection, because it'll raise an exception.
Code:
public class AutoCompleteBehavior
{
private readonly ComboBox comboBox;
private string previousSearchterm;
private object[] originalList;
public AutoCompleteBehavior(ComboBox comboBox)
{
this.comboBox = comboBox;
this.comboBox.AutoCompleteMode = AutoCompleteMode.Suggest; // crucial otherwise exceptions occur when the user types in text which is not found in the autocompletion list
this.comboBox.TextChanged += this.OnTextChanged;
this.comboBox.KeyPress += this.OnKeyPress;
this.comboBox.SelectionChangeCommitted += this.OnSelectionChangeCommitted;
}
private void OnSelectionChangeCommitted(object sender, EventArgs e)
{
if (this.comboBox.SelectedItem == null)
{
return;
}
var sel = this.comboBox.SelectedItem;
this.ResetCompletionList();
this.comboBox.SelectedItem = sel;
}
private void OnTextChanged(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(this.comboBox.Text) || !this.comboBox.Visible || !this.comboBox.Enabled)
{
return;
}
this.ResetCompletionList();
}
private void OnKeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == '\r' || e.KeyChar == '\n')
{
e.Handled = true;
if (this.comboBox.SelectedIndex == -1 && this.comboBox.Items.Count > 0
&& this.comboBox.Items[0].ToString().ToLowerInvariant().StartsWith(this.comboBox.Text.ToLowerInvariant()))
{
this.comboBox.Text = this.comboBox.Items[0].ToString();
}
this.comboBox.DroppedDown = false;
// Guardclause when detecting any enter keypresses to avoid a glitch which was selecting an item by means of down arrow key followed by enter to wipe out the text within
return;
}
// Its crucial that we use begininvoke because we need the changes to sink into the textfield Omitting begininvoke would cause the searchterm to lag behind by one character That is the last character that got typed in
this.comboBox.BeginInvoke(new Action(this.ReevaluateCompletionList));
}
private void ResetCompletionList()
{
this.previousSearchterm = null;
try
{
this.comboBox.SuspendLayout();
if (this.originalList == null)
{
this.originalList = this.comboBox.Items.Cast<object>().ToArray();
}
if (this.comboBox.Items.Count == this.originalList.Length)
{
return;
}
while (this.comboBox.Items.Count > 0)
{
this.comboBox.Items.RemoveAt(0);
}
this.comboBox.Items.AddRange(this.originalList);
}
finally
{
this.comboBox.ResumeLayout(true);
}
}
private void ReevaluateCompletionList()
{
var currentSearchterm = this.comboBox.Text.ToLowerInvariant();
if (currentSearchterm == this.previousSearchterm)
{
return;
}
this.previousSearchterm = currentSearchterm;
try
{
this.comboBox.SuspendLayout();
if (this.originalList == null)
{
this.originalList = this.comboBox.Items.Cast<object>().ToArray(); // backup original list
}
object[] newList;
if (string.IsNullOrEmpty(currentSearchterm))
{
if (this.comboBox.Items.Count == this.originalList.Length)
{
return;
}
newList = this.originalList;
}
else
{
newList = this.originalList.Where(x => x.ToString().ToLowerInvariant().Contains(currentSearchterm)).ToArray();
}
try
{
// clear list by loop through it otherwise the cursor would move to the beginning of the textbox
while (this.comboBox.Items.Count > 0)
{
this.comboBox.Items.RemoveAt(0);
}
}
catch
{
try
{
this.comboBox.Items.Clear();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
this.comboBox.Items.AddRange(newList.ToArray()); // reset list
}
finally
{
if (currentSearchterm.Length >= 1 && !this.comboBox.DroppedDown)
{
this.comboBox.DroppedDown = true; // if the current searchterm is empty we leave the dropdown list to whatever state it already had
Cursor.Current = Cursors.Default; // workaround for the fact the cursor disappears due to droppeddown=true This is a known bu.g plaguing combobox which microsoft denies to fix for years now
this.comboBox.Text = currentSearchterm; // Another workaround for a glitch which causes all text to be selected when there is a matching entry which starts with the exact text being typed in
this.comboBox.Select(currentSearchterm.Length, 0);
}
this.comboBox.ResumeLayout(true);
}
}
}
Usege:
new AutoCompleteBehavior(this.comboBoxItems);
this.comboBoxItems.Items.AddRange(new object[] { "John", "Tina", "Doctor", "Alaska" });
TIP: Can be further improved by making an extension to the ComboBox class like myCombo.ToAutoComplete()
A ComboBox,TextBox and I think a DropDownList has AutoComplete properties
Look at http://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.autocompletemode(v=vs.110).aspx
It explains which AutoCompleteMode you should use and how to set the AutoCompleteSource
You could try the following lines, it worked for me
cbxName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cbxName.AutoCompleteSource = AutoCompleteSource.ListItems;

entity framework 5 change log how to implement?

I am creating an application with MVC4 and entity framework 5. How do can I implement this?
I have looked around and found that I need to override SaveChanges .
Does anyone have any sample code on this? I am using code first approach.
As an example, the way I am saving data is as follows,
public class AuditZoneRepository : IAuditZoneRepository
{
private AISDbContext context = new AISDbContext();
public int Save(AuditZone model, ModelStateDictionary modelState)
{
if (model.Id == 0)
{
context.AuditZones.Add(model);
}
else
{
var recordToUpdate = context.AuditZones.FirstOrDefault(x => x.Id == model.Id);
if (recordToUpdate != null)
{
recordToUpdate.Description = model.Description;
recordToUpdate.Valid = model.Valid;
recordToUpdate.ModifiedDate = DateTime.Now;
}
}
try
{
context.SaveChanges();
return 1;
}
catch (Exception ex)
{
modelState.AddModelError("", "Database error has occured. Please try again later");
return -1;
}
}
}
There is no need to override SaveChanges.
You can
Trigger Context.ChangeTracker.DetectChanges(); // may be necessary depending on your Proxy approach
Then analyze the context BEFORE save.
you can then... add the Change Log to the CURRENT Unit of work.
So the log gets saved in one COMMIT transaction.
Or process it as you see fit.
But saving your change log at same time. makes sure it is ONE Transaction.
Analyzing the context sample:
I have a simple tool, to Dump context content to debug output so when in debugger I can use immediate window to check content. eg
You can use this as a starter to prepare your CHANGE Log.
Try it in debugger immediate window. I have FULL dump on my Context class.
Sample Immediate window call. UoW.Context.FullDump();
public void FullDump()
{
Debug.WriteLine("=====Begin of Context Dump=======");
var dbsetList = this.ChangeTracker.Entries();
foreach (var dbEntityEntry in dbsetList)
{
Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
switch (dbEntityEntry.State)
{
case EntityState.Detached:
case EntityState.Unchanged:
case EntityState.Added:
case EntityState.Modified:
WriteCurrentValues(dbEntityEntry);
break;
case EntityState.Deleted:
WriteOriginalValues(dbEntityEntry);
break;
default:
throw new ArgumentOutOfRangeException();
}
Debug.WriteLine("==========End of Entity======");
}
Debug.WriteLine("==========End of Context======");
}
private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
}
}
private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
}
}
}
EDIT: Get the changes
I use this routine to get chnages...
public class ObjectPair {
public string Key { get; set; }
public object Original { get; set; }
public object Current { get; set; }
}
public virtual IList<ObjectPair> GetChanges(object poco) {
var changes = new List<ObjectPair>();
var thePoco = (TPoco) poco;
foreach (var propName in Entry(thePoco).CurrentValues.PropertyNames) {
var curr = Entry(thePoco).CurrentValues[propName];
var orig = Entry(thePoco).OriginalValues[propName];
if (curr != null && orig != null) {
if (curr.Equals(orig)) {
continue;
}
}
if (curr == null && orig == null) {
continue;
}
var aChangePair = new ObjectPair {Key = propName, Current = curr, Original = orig};
changes.Add(aChangePair);
}
return changes;
}
edit 2 If you must use the Internal Object tracking.
var context = ???// YOUR DBCONTEXT class
// get objectcontext from dbcontext...
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
// for each tracked entry
foreach (var dbEntityEntry in context.ChangeTracker.Entries()) {
//get the state entry from the statemanager per changed object
var stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(dbEntityEntry.Entity);
var modProps = stateEntry.GetModifiedProperties();
Debug.WriteLine(modProps.ToString());
}
I decompiled EF6 . Get modified is indeed using private bit array to track fields that have
been changed.
// EF decompiled source..... _modifiedFields is a bitarray
public override IEnumerable<string> GetModifiedProperties()
{
this.ValidateState();
if (EntityState.Modified == this.State && this._modifiedFields != null)
{
for (int i = 0; i < this._modifiedFields.Length; ++i)
{
if (this._modifiedFields[i])
yield return this.GetCLayerName(i, this._cacheTypeMetadata);
}
}
}

How to get the project when right-click on a project/file/others in an eclipse-plugin

I'm write a simple eclipse plugin, but have a problem: When user right-click on a node(maybe a project, a file, a java compilation unit, or others), I want to get the project it belongs.
The sample code is:
public class MyAction implements IObjectActionDelegate {
private IProject project;
public void selectionChanged(IAction action, ISelection selection) {
this.project = getSelectedProject(selection);
}
public static IProject getSelectedProject(Object obj) throws Exception {
if (obj == null) {
return null;
}
if (obj instanceof IResource) {
return ((IResource) obj).getProject();
} else if (obj instanceof IStructuredSelection) {
return getSelectedProject(((IStructuredSelection) obj).getFirstElement());
}
return null;
}
}
It works at most of time, but sometimes, for example, I right-clicked on a java file, the selection will be a ICompilationUnit. Although I can add one more if in the getSelectedProject, but I don't think it's a good idea.
Is there a way to get the project of selected objects nomatter what have been selected? I don't want to add them one by one.
ICompilationUnit extends IAdaptable (see http://publib.boulder.ibm.com/infocenter/rsmhelp/v7r0m0/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/ICompilationUnit.html)
You can try and use the IAdaptable interface like that:
if (obj instanceof IAdaptable) {
IResource res = (IResource)(((IAdaptable)obj).getAdapter(IResource.class));
if (res != null) {
return res.getProject();
}
}
There are no way to convert an ICompilationUnit, IPackage, or whatever, to an IResource as there are most often no corresponding resource! E.g. for the .class elements in the navigator, the element corresponds to an entry in a JAR file or in a dependency plug-in from the target platform.
it up answer don't work, may it should be:
if (obj instanceof IStructuredSelection) {
IStructuredSelection selection1 = (IStructuredSelection)obj;
Object element = selection1.getFirstElement();
IProject project = null;
if (element instanceof IProject) {
project = (IProject) element;
} else if (element instanceof IAdaptable) {
project = (IProject) ((IAdaptable) element).getAdapter(IProject.class);
}
if (project != null) {
return project;
}
}

ReportViewer - modify toolbar?

Do anyone have good ideas of how to modify the toolbar for the WinForms version of the ReportViewer Toolbar?
That is, I want to remove some buttons and varius, but it looks like the solution is to create a brand new toolbar instead of modifying the one that is there.
Like, I had to remove export to excel, and did it this way:
// Disable excel export
foreach (RenderingExtension extension in lr.ListRenderingExtensions()) {
if (extension.Name == "Excel") {
//extension.Visible = false; // Property is readonly...
FieldInfo fi = extension.GetType().GetField("m_isVisible", BindingFlags.Instance | BindingFlags.NonPublic);
fi.SetValue(extension, false);
}
}
A bit trickysh if you ask me..
For removing toolbarbuttons, an possible way was to iterate through the Control array inside the ReportViewer and change the Visible property for the buttons to hide, but it gets reset all the time, so it is not an good way..
WHEN do MS come with an new version btw?
Yeap. You can do that in a little tricky way.
I had a task to add more scale factors to zoom report. I did it this way:
private readonly string[] ZOOM_VALUES = { "25%", "50%", "75%", "100%", "110%", "120%", "125%", "130%", "140%", "150%", "175%", "200%", "300%", "400%", "500%" };
private readonly int DEFAULT_ZOOM = 3;
//--
public ucReportViewer()
{
InitializeComponent();
this.reportViewer1.ProcessingMode = ProcessingMode.Local;
setScaleFactor(ZOOM_VALUES[DEFAULT_ZOOM]);
Control[] tb = reportViewer1.Controls.Find("ReportToolBar", true);
ToolStrip ts;
if (tb != null && tb.Length > 0 && tb[0].Controls.Count > 0 && (ts = tb[0].Controls[0] as ToolStrip) != null)
{
//here we go if our trick works (tested at .NET Framework 2.0.50727 SP1)
ToolStripComboBox tscb = new ToolStripComboBox();
tscb.DropDownStyle = ComboBoxStyle.DropDownList;
tscb.Items.AddRange(ZOOM_VALUES);
tscb.SelectedIndex = 3; //100%
tscb.SelectedIndexChanged += new EventHandler(toolStripZoomPercent_Click);
ts.Items.Add(tscb);
}
else
{
//if there is some problems - just use context menu
ContextMenuStrip cmZoomMenu = new ContextMenuStrip();
for (int i = 0; i < ZOOM_VALUES.Length; i++)
{
ToolStripMenuItem tsmi = new ToolStripMenuItem(ZOOM_VALUES[i]);
tsmi.Checked = (i == DEFAULT_ZOOM);
//tsmi.Tag = (IntPtr)cmZoomMenu;
tsmi.Click += new EventHandler(toolStripZoomPercent_Click);
cmZoomMenu.Items.Add(tsmi);
}
reportViewer1.ContextMenuStrip = cmZoomMenu;
}
}
private bool setScaleFactor(string value)
{
try
{
int percent = Convert.ToInt32(value.TrimEnd('%'));
reportViewer1.ZoomMode = ZoomMode.Percent;
reportViewer1.ZoomPercent = percent;
return true;
}
catch
{
return false;
}
}
private void toolStripZoomPercent_Click(object sender, EventArgs e)
{
ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
ToolStripComboBox tscb = sender as ToolStripComboBox;
if (tscb != null && tscb.SelectedIndex > -1)
{
setScaleFactor(tscb.Items[tscb.SelectedIndex].ToString());
}
else if (tsmi != null)
{
if (setScaleFactor(tsmi.Text))
{
foreach (ToolStripItem tsi in tsmi.Owner.Items)
{
ToolStripMenuItem item = tsi as ToolStripMenuItem;
if (item != null && item.Checked)
{
item.Checked = false;
}
}
tsmi.Checked = true;
}
else
{
tsmi.Checked = false;
}
}
}
Get the toolbar from ReportViewer control:
ToolStrip toolStrip = (ToolStrip)reportViewer.Controls.Find("toolStrip1", true)[0]
Add new items:
toolStrip.Items.Add(...)
There are a lot of properties to set which buttons would you like to see.
For example ShowBackButton, ShowExportButton, ShowFindControls, and so on. Check them in the help, all starts with "Show".
But you are right, you cannot add new buttons. You have to create your own toolbar in order to do this.
What do you mean about new version? There is already a 2008 SP1 version of it.
Another way would be to manipulate the generated HTML at runtime via javascript. It's not very elegant, but it does give you full control over the generated HTML.
For VS2013 web ReportViewer V11 (indicated as rv), the code below adds a button.
private void AddPrintBtn()
{
foreach (Control c in rv.Controls)
{
foreach (Control c1 in c.Controls)
{
foreach (Control c2 in c1.Controls)
{
foreach (Control c3 in c2.Controls)
{
if (c3.ToString() == "Microsoft.Reporting.WebForms.ToolbarControl")
{
foreach (Control c4 in c3.Controls)
{
if (c4.ToString() == "Microsoft.Reporting.WebForms.PageNavigationGroup")
{
var btn = new Button();
btn.Text = "Criteria";
btn.ID = "btnFlip";
btn.OnClientClick = "$('#pnl').toggle();";
c4.Controls.Add(btn);
return;
}
}
}
}
}
}
}
}
I had this question for al ong time I I found the answer after a long tie and the main source of kowledge I used was this webpega: I'd like to thank you all guys adding the code that allowed me to do it and a picture with the result.
Instead of using the ReportViewer Class, you need to create a new classs, in my case, I named it ReportViewerPlus and it goes like this:
using Microsoft.Reporting.WinForms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace X
{
class ReportViewerPlus : ReportViewer
{
private Button boton { get; set; }
public ReportViewerPlus(Button but) {
this.boton = but;
testc(this.Controls[0]);
}
public ReportViewerPlus()
{
}
private void testc(Control item){
if(item is ToolStrip)
{
ToolStripItemCollection tsic = ((ToolStrip)item).Items;
tsic.Insert(0, new ToolStripControlHost(boton));
return;
}
for (int i = 0; i < item.Controls.Count; i++)
{
testc(item.Controls[i]);
}
}
}
}
You have to add the button directly in the constructor of the class and you can configure the button in your designer.
Here's a pic of the result, not perfect, but enough to go(safe link I swear, but I can't post my own pics, don't have enough reputation).
http://prntscr.com/5lfssj
If you look carefully in the code of the class, you'd see more or less how it works and you could make your changes and make it possible to establish it in other site of the toolbar.
Thank you so much for helping me in the past, I hope this helps lots of people!
Generally you are suppose to create your own toolbar if you want to modify it. Your solution for removing buttons will probably work if that is all you need to do, but if you want to add your own you should probably just bite the bullet and build a replacement.
You may modify reportviewer controls by CustomizeReportToolStrip method.
this example remove Page Setup Button, Page Layout Button in WinForm
public CustOrderReportForm() {
InitializeComponent();
CustomizeReport(this.reportViewer1);
}
private void CustomizeReport(Control reportControl, int recurCount = 0) {
Console.WriteLine("".PadLeft(recurCount + 1, '.') + reportControl.GetType() + ":" + reportControl.Name);
if (reportControl is Button) {
CustomizeReportButton((Button)reportControl, recurCount);
}
else if (reportControl is ToolStrip) {
CustomizeReportToolStrip((ToolStrip)reportControl, recurCount);
}
foreach (Control childControl in reportControl.Controls) {
CustomizeReport(childControl, recurCount + 1);
}
}
//-------------------------------------------------------------
void CustomizeReportToolStrip(ToolStrip c, int recurCount) {
List<ToolStripItem> customized = new List<ToolStripItem>();
foreach (ToolStripItem i in c.Items) {
if (CustomizeReportToolStripItem(i, recurCount + 1)) {
customized.Add(i);
}
}
foreach (var i in customized) c.Items.Remove(i);
}
//-------------------------------------------------------------
void CustomizeReportButton(Button button, int recurCount) {
}
//-------------------------------------------------------------
bool CustomizeReportToolStripItem(ToolStripItem i, int recurCount) {
Console.WriteLine("".PadLeft(recurCount + 1, '.') + i.GetType() + ":" + i.Name);
if (i.Name == "pageSetup") {
return true;
}
else if (i.Name == "printPreview") {
return true;
}
return false; ;
}