LongListSelector WP8 ItemRealized -infinite scrollable list- - xaml

I am trying to implement a infinitive scrollable List with dynamic load. (like http://blogs.windows.com/windows_phone/b/wpdev/archive/2012/10/01/how-to-create-an-infinite-scrollable-list-with-longlistselector.aspx )
The DataSource is bound to a ObservableCollection
The filling of the list starts with adding items to the collection
The ItemRealizedEvent starts further fillings of the ObservableCollection
I thought the ItemrealizedEvent is triggered by scrolling BUT it triggers always after adding items to the collection for each item.
--> So its not dynamic, it just loads everything
any Ideas?
Within PageClass:
within the Constructor:
(...)
LLS_BooksListAll.DataContext = _viewModel.SearchAllViewModel;
LLS_BooksListAll.ItemsSource = _viewModel.SearchAllViewModel.MediumCollection;
(...)
private async void LLS_BooksListAll_ItemRealized(object sender, ItemRealizationEventArgs e)
{
if ((LLS_BooksListAll.ItemsSource as ObservableCollection<Medium>) == null) return;
//get number of loaded items
int currentItemsCount = (LLS_BooksListAll.ItemsSource as ObservableCollection<Medium>).Count;
if (!_viewModel.SearchAllViewModel.IsLoading && currentItemsCount >= _offsetKnob &&
(e.Container.Content as Medium) != null)
{
if ((e.Container.Content as Medium).Equals((LLS_BooksListAll.ItemsSource as
ObservableCollection<Medium>)[currentItemsCount - _offsetKnob]))
{
_pageNumberAll++;
try
{
await _viewModel.SearchAllViewModel.SearchAll(TB_Search.Text, _pageNumberAll);
}
catch (RestException ex)
{
MessageBox.Show("Connection-Error: LLS_BooksListAll_ItemRealized - " + ex.Message);
}
}
}
}
Within ViewModelClass:
public async void SearchAll(string searchword, int pageNumber)
{
if (pageNumber == 1) this.MediumCollection.Clear();
IsLoading = true;
SearchRequest search = new SearchRequest();
String responseString = await search.Get(searchword, SearchRange.all, pageNumber);
MediaUser response = JsonConvert.DeserializeObject<MediaUser>(responseString);
foreach (Medium med in response.media)
{
MediumCollection.Add(med); //Filling the observable collection
}
IsLoading = false;
}

ItemRealized have an extrange behavior but actually it Works. It preload a bunch of elementos ahead of the screen scroll so if you are seeing 10 elements, ItemRealized preload 30 or 40 and the stop until you scroll to preload more items before you reach the end of the list. If you test it with 100 elements you could see this behavior.

Related

Show a toast message when a text is recognized from camera

I am trying to detect a text with a specific format from a live camera feed and show a toast message when that text is detected automatically.
I was able to detect the text and put a box around it. But I'm having a hard time showing that toast message.
This is the receiveDetections method from the Processor
#Override
public void receiveDetections(Detector.Detections<TextBlock> detections) {
mGraphicOverlay.clear();
SparseArray<TextBlock> items = detections.getDetectedItems();
for (int i = 0; i < items.size(); ++i) {
TextBlock item = items.valueAt(i);
if (item != null && item.getValue() != null) {
Log.d("OcrDetectorProcessor", "Text detected! " + item.getValue());
// Check if it is the correct format
if (item.getValue().matches("^\\d{3} \\d{3} \\d{4} \\d{4}")){
OcrGraphic graphic = new OcrGraphic(mGraphicOverlay, item);
mGraphicOverlay.add(graphic);
// Show the toast message
}
}
}
}
-> Showing a toast is not my end goal, If I'm able to fix that I'll fix the main problem.
-> I'm building on top of the code labs tutorial for the text vision api
First pass context to OcrDetectorProcessor class from OcrCaptureActivity and runUiThread from that context. This piece of code show all text at once. If you want to show words one by one you need to split from TextBlock items.
Context context;
OcrDetectorProcessor(GraphicOverlay<OcrGraphic> ocrGraphicOverlay, Context context) {
mGraphicOverlay = ocrGraphicOverlay;
this.context = context;
}
#Override
public void receiveDetections(Detector.Detections<TextBlock> detections) {
mGraphicOverlay.clear();
final String result;
String detectedText = "";
SparseArray<TextBlock> items = detections.getDetectedItems();
for (int i = 0; i < items.size(); ++i) {
final TextBlock item = items.valueAt(i);
OcrGraphic graphic = new OcrGraphic(mGraphicOverlay, item);
mGraphicOverlay.add(graphic);
detectedText += item.getValue();
}
result = detectedText;
((OcrCaptureActivity)context).runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(context, result, Toast.LENGTH_SHORT).show();
}
});
}

ListView flickering when updating binding collection

I am working on a Windows 10 Universal app and see some flickering issues when I use a ListView in my app. My ListView is using x:Bind to bind to an ObservableCollection in my View Model.
When user performs some actions, or a background update occurs, I do some processing that requires the ObservableCollection to be refreshed.
private ObservableCollection<Item> UIItems = new ObservableCollection<Item>();
private bool IsUpdating = false;
private void UpdateUIProperties(List<Item> newItems)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
IsUpdating = true;
UIItems.Clear();
foreach (var item in newItems)
{
if (item.IsVisible)
{
UIItems.Add(item);
}
}
IsUpdating = false;
});
}
After this code gets executed, the ListView flickers and then the Scrollviewer goes all the way to the top. Is there any way to prevent this and have the ListView's ScrollViewer stay at its original offset?
A solution that seem to work for me is to bind the Itemsource to an Observable collection and then have another collection that contains the items that you want to add. Have the Item in the collection implement the interface below. When you want to update the collection use the MergeCollection method to make sure the items in the collection are preserved, but they have the new config.
public interface IConfigureFrom<T>
{
void ConfigureFrom(T other);
}
public static void MergeCollection<T>(ICollection<T> source, ICollection<T> dest) where T : IConfigureFrom<T>, new()
{
// First remove entries at the bottom of the dest list that are no longer there
if (dest.Count > source.Count)
{
for (int i = dest.Count - 1; i >= source.Count; i--)
{
var coll = dest as Collection<T>;
if (coll != null)
{
coll.RemoveAt(i);
}
else
{
dest.Remove(dest.Last());
}
}
}
// reconfigure existing entries with the new configureation
var sourecList = source.ToList();
var destList = dest.ToList();
for (int i = dest.Count - 1; i >= 0; i--)
{
var target = destList[i];
var config = sourecList[i];
target.ConfigureFrom(config);
}
// add new entries at the end and configure them from the source list
for (int i = dest.Count; i < source.Count; i++)
{
T newItem = new T();
newItem.ConfigureFrom(sourecList[i]);
dest.Add(newItem);
}
}
When changing all items in your ListView, it is usually better to just swap the whole ItemsSource.
Just set:
UIItems = new List<...>(your data);
And have it fire OnNotifyPropertyChanged of course.

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;

Copying sitecore rendering to new template programmatically using renderingDefinition.ItemId?

I have a custom sitecore button which changes the template of the current item, simple enough.
However as part of this I'm trying to also migrate the renderings of the old layout to a new layout if it's of a certain sublayout type by ItemId. However the ItemId that is returned is always null, the only value I get back from the RenderingDefinition is the UniqueId.
What am I doing wrong?
I have used this blog post as a guide.
The Code
public class ConvertToNewTemplateCommand : Command
{
protected void Run(ClientPipelineArgs args)
{
if (!SheerResponse.CheckModified())
return;
Item item = Context.ContentDatabase.Items[args.Parameters["id"]];
if (args.IsPostBack)
{
if (args.Result == "yes")
{
//Get current layout details
var originalLayoutXml = item[FieldIDs.LayoutField];
//Get new template
TemplateItem hubTemplate = Context.ContentDatabase.GetTemplate("some guid...");
//Change template
item.ChangeTemplate(hubTemplate);
//Reset laytout
ResetLayout(item);
//Get reset layout
var newLayoutXml = item[FieldIDs.LayoutField];
//Add all the module containers to the new layout in the central column
MoveModuleContainers(item, originalLayoutXml, newLayoutXml);
}
}
}
private void MoveModuleContainers(Item item, string oldXml, string newXml)
{
var oldLayout = LayoutDefinition.Parse(oldXml);
var newLayout = LayoutDefinition.Parse(newXml);
bool updated = false;
var oldRenderings = (oldLayout.Devices[0] as DeviceDefinition).Renderings;
var newRenderings = (newLayout.Devices[0] as DeviceDefinition).Renderings;
foreach (RenderingDefinition rendering in oldRenderings)
{
// Here is where the rendering.ItemID is always null
if (rendering != null && !String.IsNullOrEmpty(rendering.ItemID) && new Guid(rendering.ItemID) == new Guid("matching guid..."))
{
rendering.Placeholder = "middlecolumn";
newRenderings.Add(rendering);
updated = true;
}
}
if (updated)
{
// Save item...
}
}
}
I got onto Sitecore support in the end which informed me that I should use:
Sitecore.Data.Fields.LayoutField.GetFieldValue(item.Fields[Sitecore.FieldIDs.LayoutField])
instead of:
item[FieldIDs.LayoutField]
to get the items layoutField correctly. This results in the rendering values being parsed correctly and as they say the rest is history.

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; ;
}