Related
I am using XR class from Unity and trying to add all button inputs to m_SelectUsage. But unfortunately it does not take an array of values. Here in the picture as you can see, only one button can be used "Grip". Is there a way to add all the button inputs to this class?
using UnityEngine.SpatialTracking;
#if LIH_PRESENT
using UnityEngine.Experimental.XR.Interaction;
#endif
namespace UnityEngine.XR.Interaction.Toolkit
{
/// <summary>
/// <see cref="XRBaseController"/> <see cref="MonoBehaviour"/> that interprets
/// feature values on an input device in the XR input subsystem into
/// XR Interaction Interactor position, rotation, and interaction states.
/// </summary>
[AddComponentMenu("XR/XR Controller (Device-based)")]
public class XRController : XRBaseController
{
[SerializeField]
[Tooltip("The XRNode for this controller.")]
XRNode m_ControllerNode = XRNode.RightHand;
/// <summary>
/// The <see cref="XRNode"/> for this controller.
/// </summary>
public XRNode controllerNode
{
get => m_ControllerNode;
set => m_ControllerNode = value;
}
[SerializeField]
[Tooltip("The input to use for detecting a select.")]
InputHelpers.Button m_SelectUsage = InputHelpers.Button.Grip;
/// <summary>
/// The input to use for detecting a select.
/// </summary>
public InputHelpers.Button selectUsage
{
get => m_SelectUsage;
set => m_SelectUsage = value;
}
[SerializeField]
[Tooltip("The input to use for detecting activation.")]
InputHelpers.Button m_ActivateUsage = InputHelpers.Button.Trigger;
/// <summary>
/// The input to use for detecting activation.
/// </summary>
public InputHelpers.Button activateUsage
{
get => m_ActivateUsage;
set => m_ActivateUsage = value;
}
[SerializeField]
[Tooltip("The input to use for detecting a UI press.")]
InputHelpers.Button m_UIPressUsage = InputHelpers.Button.Trigger;
/// <summary>
/// The input to use for detecting a UI press.
/// </summary>
public InputHelpers.Button uiPressUsage
{
get => m_UIPressUsage;
set => m_UIPressUsage = value;
}
[SerializeField]
[Tooltip("The amount an axis needs to be pressed to trigger an interaction event.")]
float m_AxisToPressThreshold = 0.1f;
/// <summary>
/// The amount an axis needs to be pressed to trigger an interaction event.
/// </summary>
public float axisToPressThreshold
{
get => m_AxisToPressThreshold;
set => m_AxisToPressThreshold = value;
}
[SerializeField]
[Tooltip("The input to use to rotate an anchor to the Left.")]
InputHelpers.Button m_RotateAnchorLeft = InputHelpers.Button.PrimaryAxis2DLeft;
/// <summary>
/// The input to use to rotate an anchor to the Left.
/// </summary>
public InputHelpers.Button rotateObjectLeft
{
get => m_RotateAnchorLeft;
set => m_RotateAnchorLeft = value;
}
[SerializeField]
[Tooltip("The input to use to rotate an anchor to the Right.")]
InputHelpers.Button m_RotateAnchorRight = InputHelpers.Button.PrimaryAxis2DRight;
/// <summary>
/// The input to use to rotate an anchor to the Right.
/// </summary>
public InputHelpers.Button rotateObjectRight
{
get => m_RotateAnchorRight;
set => m_RotateAnchorRight = value;
}
[SerializeField]
[Tooltip("The input that will be used to translate the anchor away from the interactor.")]
InputHelpers.Button m_MoveObjectIn = InputHelpers.Button.PrimaryAxis2DUp;
/// <summary>
/// The input that will be used to translate the anchor away from the interactor.
/// </summary>
public InputHelpers.Button moveObjectIn
{
get => m_MoveObjectIn;
set => m_MoveObjectIn = value;
}
[SerializeField]
[Tooltip("The input that will be used to translate the anchor towards the interactor.")]
InputHelpers.Button m_MoveObjectOut = InputHelpers.Button.PrimaryAxis2DDown;
/// <summary>
/// The input that will be used to translate the anchor towards the interactor.
/// </summary>
public InputHelpers.Button moveObjectOut
{
get => m_MoveObjectOut;
set => m_MoveObjectOut = value;
}
#if LIH_PRESENT
[SerializeField, Tooltip("Pose provider used to provide tracking data separate from the XR Node.")]
BasePoseProvider m_PoseProvider;
/// <summary>
/// Pose provider used to provide tracking data separate from the <see cref="XRNode"/>.
/// </summary>
public BasePoseProvider poseProvider
{
get => m_PoseProvider;
set => m_PoseProvider = value;
}
#endif
InputDevice m_InputDevice;
/// <summary>
/// (Read Only) The <see cref="InputDevice"/> being used to read data from.
/// </summary>
public InputDevice inputDevice => m_InputDevice.isValid ? m_InputDevice : m_InputDevice = InputDevices.GetDeviceAtXRNode(controllerNode);
/// <inheritdoc />
protected override void UpdateTrackingInput(XRControllerState controllerState)
{
controllerState.poseDataFlags = PoseDataFlags.NoData;
#if LIH_PRESENT_V1API
if (m_PoseProvider != null)
{
if (m_PoseProvider.TryGetPoseFromProvider(out var poseProviderPose))
{
controllerState.position = poseProviderPose.position;
controllerState.rotation = poseProviderPose.rotation;
controllerState.poseDataFlags = PoseDataFlags.Position | PoseDataFlags.Rotation;
}
}
else
#elif LIH_PRESENT_V2API
if (m_PoseProvider != null)
{
var retFlags = m_PoseProvider.GetPoseFromProvider(out var poseProviderPose);
if ((retFlags & PoseDataFlags.Position) != 0)
{
controllerState.position = poseProviderPose.position;
controllerState.poseDataFlags |= PoseDataFlags.Position;
}
if ((retFlags & PoseDataFlags.Rotation) != 0)
{
controllerState.rotation = poseProviderPose.rotation;
controllerState.poseDataFlags |= PoseDataFlags.Rotation;
}
}
else
#endif
{
if (inputDevice.TryGetFeatureValue(CommonUsages.devicePosition, out controllerState.position))
{
controllerState.poseDataFlags |= PoseDataFlags.Position;
}
if (inputDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out controllerState.rotation))
{
controllerState.poseDataFlags |= PoseDataFlags.Rotation;
}
}
}
/// <inheritdoc />
protected override void UpdateInput(XRControllerState controllerState)
{
controllerState.ResetFrameDependentStates();
HandleInteractionAction(m_SelectUsage, ref controllerState.selectInteractionState);
HandleInteractionAction(m_ActivateUsage, ref controllerState.activateInteractionState);
HandleInteractionAction(m_UIPressUsage, ref controllerState.uiPressInteractionState);
}
void HandleInteractionAction(InputHelpers.Button button, ref InteractionState interactionState)
{
inputDevice.IsPressed(button, out var pressed, m_AxisToPressThreshold);
if (pressed)
{
Debug.Log("Detect");
if (!interactionState.active)
{
interactionState.activatedThisFrame = true;
interactionState.active = true;
}
}
else
{
if (interactionState.active)
{
interactionState.deactivatedThisFrame = true;
interactionState.active = false;
}
}
}
/// <inheritdoc />
public override bool SendHapticImpulse(float amplitude, float duration)
{
if (inputDevice.TryGetHapticCapabilities(out var capabilities) &&
capabilities.supportsImpulse)
{
return inputDevice.SendHapticImpulse(0u, amplitude, duration);
}
return false;
}
}
}
You can overwrite XRController to accept an array for each of the input values.
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class MyXRController : XRController
{
[SerializeField]
InputHelpers.Button[] m_SelectUsageArray;
protected override void UpdateInput(XRControllerState controllerState)
{
// Because we use base input m_SelectUsage will still be accepted if provided
base.UpdateInput(controllerState);
// Iterate over all buttons
foreach (var selectUsage in m_SelectUsageArray)
{
HandleInteractionAction(selectUsage, ref controllerState.selectInteractionState);
}
}
}
You can overwrite other interactions in a similar manner.
Then in your GameObject, use MyXRController instead of XRController
I have an issue with an textbox, once it was to accept just 3 values know i have update it to nvarchar(MAX) I updated the EntityModelDB but still after this i'm receivein an error when i add more than 3 values in my textbox
Here is my code
if (!Regex.Match(item.treatments_code, #"^[A-Z0-9\-]{6}$").Success)
{
ret = false;
errors = errors.Concat(new string[] { language.text003 }).ToArray();
}
Here is my error
An exception of type 'System.ArgumentException' occurred in DGUIGHF.dll but was not handled in user code
Additional information: Invalid code format. 3 character, uppercase letters [A-Z] or numbers [0-9], or minus '-'.
FormTreatments.cs
using DG.Data.Model.Helpers;
using DG.DentneD.Forms.Objects;
using DG.DentneD.Helpers;
using DG.DentneD.Model;
using DG.DentneD.Model.Entity;
using DG.UI.GHF;
using SMcMaster;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using Zuby.ADGV;
namespace DG.DentneD.Forms
{
public partial class FormTreatments : DGUIGHFForm
{
private DentneDModel _dentnedModel = null;
private TabElement tabElement_tabTreatments = new TabElement();
private TabElement tabElement_tabTreatmentsPrices = new TabElement();
private readonly BoxLoader _boxLoader = null;
/// <summary>
/// Constructor
/// </summary>
public FormTreatments()
{
InitializeComponent();
(new TabOrderManager(this)).SetTabOrder(TabOrderManager.TabScheme.AcrossFirst);
Initialize(Program.uighfApplication);
_dentnedModel = new DentneDModel();
_dentnedModel.LanguageHelper.LoadFromFile(Program.uighfApplication.LanguageFilename);
_boxLoader = new BoxLoader(_dentnedModel);
}
/// <summary>
/// Add components language
/// </summary>
public override void AddLanguageComponents()
{
//main
LanguageHelper.AddComponent(this);
LanguageHelper.AddComponent(treatmentsidDataGridViewTextBoxColumn, this.GetType().Name, "HeaderText");
LanguageHelper.AddComponent(nameDataGridViewTextBoxColumn, this.GetType().Name, "HeaderText");
LanguageHelper.AddComponent(codeDataGridViewTextBoxColumn, this.GetType().Name, "HeaderText");
LanguageHelper.AddComponent(typeDataGridViewTextBoxColumn, this.GetType().Name, "HeaderText");
LanguageHelper.AddComponent(button_export);
//tabTreatments
LanguageHelper.AddComponent(tabPage_tabTreatments);
LanguageHelper.AddComponent(button_tabTreatments_new);
LanguageHelper.AddComponent(button_tabTreatments_edit);
LanguageHelper.AddComponent(button_tabTreatments_delete);
LanguageHelper.AddComponent(button_tabTreatments_save);
LanguageHelper.AddComponent(button_tabTreatments_cancel);
LanguageHelper.AddComponent(treatments_idLabel);
LanguageHelper.AddComponent(treatments_nameLabel);
LanguageHelper.AddComponent(treatments_codeLabel);
LanguageHelper.AddComponent(treatmentstypes_idLabel);
LanguageHelper.AddComponent(treatments_mexpirationLabel);
LanguageHelper.AddComponent(treatments_mexpirationinfoLabel);
LanguageHelper.AddComponent(treatments_priceLabel);
LanguageHelper.AddComponent(treatments_notesLabel);
LanguageHelper.AddComponent(taxes_idLabel);
LanguageHelper.AddComponent(treatments_isunitpriceCheckBox);
LanguageHelper.AddComponent(button_tabTreatments_unsettaxesid);
//tabTreatmentsPrices
LanguageHelper.AddComponent(tabPage_tabTreatmentsPrices);
LanguageHelper.AddComponent(label_tabTreatmentsPrices_filterpriceslists);
LanguageHelper.AddComponent(treatmentspricesidDataGridViewTextBoxColumn, this.GetType().Name, "HeaderText");
LanguageHelper.AddComponent(pricelistDataGridViewTextBoxColumn, this.GetType().Name, "HeaderText");
LanguageHelper.AddComponent(priceDataGridViewTextBoxColumn, this.GetType().Name, "HeaderText");
LanguageHelper.AddComponent(button_tabTreatmentsPrices_new);
LanguageHelper.AddComponent(button_tabTreatmentsPrices_edit);
LanguageHelper.AddComponent(button_tabTreatmentsPrices_delete);
LanguageHelper.AddComponent(button_tabTreatmentsPrices_save);
LanguageHelper.AddComponent(button_tabTreatmentsPrices_cancel);
LanguageHelper.AddComponent(treatmentsprices_idLabel);
LanguageHelper.AddComponent(treatmentspriceslists_idLabel);
LanguageHelper.AddComponent(treatmentsprices_priceLabel);
}
/// <summary>
/// Form language dictionary
/// </summary>
public class FormLanguage : IDGUIGHFLanguage
{
public string exportColumnCode = "Code";
public string exportColumnType = "Type";
public string exportColumnName = "Name";
public string exportColumnPrice = "Price";
public string exportSaveFileDialogTitle = "Save an Excel File";
public string exportErrorMessage = "Error writing file '{0}'.";
public string exportErrorTitle = "Error";
public string exportSuccessMessage = "File created. Do you want to open it with your default application?";
public string exportSuccessTitle = "Open";
}
/// <summary>
/// Form language
/// </summary>
public FormLanguage language = new FormLanguage();
/// <summary>
/// Initialize TabElements
/// </summary>
protected override void InitializeTabElements()
{
//set Readonly OnSetEditingMode for Controls
DisableReadonlyCheckOnSetEditingModeControlCollection.Add(typeof(DataGridView));
DisableReadonlyCheckOnSetEditingModeControlCollection.Add(typeof(AdvancedDataGridView));
//set Main BindingSource
BindingSourceMain = vTreatmentsBindingSource;
GetDataSourceMain = GetDataSource_main;
//set Main TabControl
TabControlMain = tabControl_main;
//set Main Panels
PanelFiltersMain = panel_filters;
PanelListMain = panel_list;
PanelsExtraMain = null;
//set tabTreatments
tabElement_tabTreatments = new TabElement()
{
TabPageElement = tabPage_tabTreatments,
ElementItem = new TabElement.TabElementItem()
{
PanelData = panel_tabTreatments_data,
PanelActions = panel_tabTreatments_actions,
PanelUpdates = panel_tabTreatments_updates,
ParentBindingSourceList = vTreatmentsBindingSource,
GetParentDataSourceList = GetDataSource_main,
BindingSourceEdit = treatmentsBindingSource,
GetDataSourceEdit = GetDataSourceEdit_tabTreatments,
AfterSaveAction = AfterSaveAction_tabTreatments,
AddButton = button_tabTreatments_new,
UpdateButton = button_tabTreatments_edit,
RemoveButton = button_tabTreatments_delete,
SaveButton = button_tabTreatments_save,
CancelButton = button_tabTreatments_cancel,
Add = Add_tabTreatments,
Update = Update_tabTreatments,
Remove = Remove_tabTreatments
}
};
//set tabTreatmentsPrices
tabElement_tabTreatmentsPrices = new TabElement()
{
TabPageElement = tabPage_tabTreatmentsPrices,
ElementListItem = new TabElement.TabElementListItem()
{
PanelFilters = panel_tabTreatmentsPrices_filters,
PanelList = panel_tabTreatmentsPrices_list,
PanelData = panel_tabTreatmentsPrices_data,
PanelActions = panel_tabTreatmentsPrices_actions,
PanelUpdates = panel_tabTreatmentsPrices_updates,
BindingSourceList = vTreatmentsPricesBindingSource,
GetDataSourceList = GetDataSourceList_tabTreatmentsPrices,
BindingSourceEdit = treatmentspricesBindingSource,
GetDataSourceEdit = GetDataSourceEdit_tabTreatmentsPrices,
AfterSaveAction = AfterSaveAction_tabTreatmentsPrices,
AddButton = button_tabTreatmentsPrices_new,
IsAddButtonDefaultClickEventAttached = false,
UpdateButton = button_tabTreatmentsPrices_edit,
RemoveButton = button_tabTreatmentsPrices_delete,
SaveButton = button_tabTreatmentsPrices_save,
CancelButton = button_tabTreatmentsPrices_cancel,
Add = Add_tabTreatmentsPrices,
Update = Update_tabTreatmentsPrices,
Remove = Remove_tabTreatmentsPrices
}
};
//set Elements
TabElements.Add(tabElement_tabTreatments);
TabElements.Add(tabElement_tabTreatmentsPrices);
}
/// <summary>
/// Loader
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FormTreatments_Load(object sender, EventArgs e)
{
IsBindingSourceLoading = true;
advancedDataGridView_main.SortASC(advancedDataGridView_main.Columns[2]);
advancedDataGridView_main.SortASC(advancedDataGridView_main.Columns[1]);
advancedDataGridView_main.SortASC(advancedDataGridView_main.Columns[3]);
IsBindingSourceLoading = false;
PreloadView();
ReloadView();
}
/// <summary>
/// Preload View
/// </summary>
private void PreloadView()
{
IsBindingSourceLoading = true;
_boxLoader.LoadComboBoxTreatmentsTypes(treatmentstypes_idComboBox);
_boxLoader.LoadComboBoxTreatmentsPricesLists(treatmentspriceslists_idComboBox);
_boxLoader.LoadComboBoxTaxes(taxes_idComboBox);
_boxLoader.LoadComboBoxFilterTreatmentsPricesLists(comboBox_tabTreatmentsPrices_filterPriceslists);
IsBindingSourceLoading = false;
}
/// <summary>
/// Reset all the tab datagrid
/// </summary>
private void ResetTabsDataGrid()
{
IsBindingSourceLoading = true;
advancedDataGridView_tabTreatmentsPrices_list.CleanFilterAndSort();
advancedDataGridView_tabTreatmentsPrices_list.SortASC(advancedDataGridView_tabTreatmentsPrices_list.Columns[1]);
IsBindingSourceLoading = false;
}
/// <summary>
/// Get main list DataSource
/// </summary>
/// <returns></returns>
private object GetDataSource_main()
{
ResetTabsDataGrid();
IEnumerable<VTreatments> vTreatments =
_dentnedModel.Treatments.List().Select(
r => new VTreatments
{
treatments_id = r.treatments_id,
code = r.treatments_code,
type = _dentnedModel.TreatmentsTypes.Find(r.treatmentstypes_id).treatmentstypes_name,
name = r.treatments_name
}).ToList();
return DGDataTableUtils.ToDataTable<VTreatments>(vTreatments);
}
/// <summary>
/// Main list current element changed hanlder
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void vTreatmentsBindingSource_CurrentChanged(object sender, EventArgs e)
{
if (IsBindingSourceLoading)
return;
//get current itme
int treatments_id = -1;
if (vTreatmentsBindingSource.Current != null)
{
treatments_id = (((DataRowView)vTreatmentsBindingSource.Current).Row).Field<int>("treatments_id");
}
//set treatments fields
treatments_mexpirationTextBox.Text = "";
if (treatments_id != -1)
{
treatments treatment = _dentnedModel.Treatments.Find(treatments_id);
treatments_mexpirationTextBox.Text = treatment.treatments_mexpiration.ToString();
}
//reset treatments prices filter
comboBox_tabTreatmentsPrices_filterPriceslists.SelectedIndex = -1;
}
/// <summary>
/// Export click
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_export_Click(object sender, EventArgs e)
{
string filename = null;
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "Excel|*.xls";
saveFileDialog.Title = language.exportSaveFileDialogTitle; ;
saveFileDialog.ShowDialog();
filename = saveFileDialog.FileName;
if (!String.IsNullOrEmpty(filename))
{
Cursor.Current = Cursors.WaitCursor;
DataTable datatable = new DataTable();
datatable.Clear();
datatable.Columns.Add(language.exportColumnCode);
datatable.Columns.Add(language.exportColumnType);
datatable.Columns.Add(language.exportColumnName);
datatable.Columns.Add(language.exportColumnPrice);
foreach (treatmentspriceslists treatmentspriceslist in _dentnedModel.TreatmentsPricesLists.List().OrderBy(r => r.treatmentspriceslists_name))
{
datatable.Columns.Add(language.exportColumnPrice + "-" + treatmentspriceslist.treatmentspriceslists_id);
}
//add datatable columns
foreach (treatments treatment in _dentnedModel.Treatments.List().OrderBy(r => r.treatments_code))
{
DataRow row = datatable.NewRow();
row[language.exportColumnCode] = treatment.treatments_code;
row[language.exportColumnType] = _dentnedModel.TreatmentsTypes.Find(treatment.treatmentstypes_id).treatmentstypes_name;
row[language.exportColumnName] = treatment.treatments_name;
row[language.exportColumnPrice] = treatment.treatments_price;
foreach (treatmentspriceslists treatmentspriceslist in _dentnedModel.TreatmentsPricesLists.List().OrderBy(r => r.treatmentspriceslists_name))
{
Nullable<decimal> price = null;
treatmentsprices treatmentsprice = _dentnedModel.TreatmentsPrices.FirstOrDefault(r => r.treatments_id == treatment.treatments_id && r.treatmentspriceslists_id == treatmentspriceslist.treatmentspriceslists_id);
if (treatmentsprice != null)
{
price = treatmentsprice.treatmentsprices_price;
}
row[language.exportColumnPrice + "-" + treatmentspriceslist.treatmentspriceslists_id] = price;
}
datatable.Rows.Add(row);
}
Cursor.Current = Cursors.Default;
//export to excel
DataSet dataset = new DataSet();
dataset.Tables.Add(datatable);
if (!String.IsNullOrEmpty(filename))
{
try
{
ExcelExporter.CreateWorkbook(filename, dataset);
}
catch
{
MessageBox.Show(String.Format(language.exportErrorMessage, filename), language.exportErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (MessageBox.Show(language.exportSuccessMessage, language.exportSuccessTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
{
try
{
Process.Start(filename);
}
catch { }
}
}
}
}
#region tabTreatments
/// <summary>
/// Load the tab DataSource
/// </summary>
/// <returns></returns>
private object GetDataSourceEdit_tabTreatments()
{
return DGUIGHFData.LoadEntityFromCurrentBindingSource<treatments, DentneDModel>(_dentnedModel.Treatments, vTreatmentsBindingSource, new string[] { "treatments_id" });
}
/// <summary>
/// Do actions after Save
/// </summary>
/// <param name="item"></param>
private void AfterSaveAction_tabTreatments(object item)
{
DGUIGHFData.SetBindingSourcePosition<treatments, DentneDModel>(_dentnedModel.Treatments, item, vTreatmentsBindingSource);
}
/// <summary>
/// Add an item
/// </summary>
/// <param name="item"></param>
private void Add_tabTreatments(object item)
{
DGUIGHFData.Add<treatments, DentneDModel>(_dentnedModel.Treatments, item);
//update mexpiration
if (!String.IsNullOrEmpty(treatments_mexpirationTextBox.Text))
((treatments)item).treatments_mexpiration = Convert.ToByte(treatments_mexpirationTextBox.Text);
else
((treatments)item).treatments_mexpiration = null;
}
/// <summary>
/// Update an item
/// </summary>
/// <param name="item"></param>
private void Update_tabTreatments(object item)
{
//update mexpiration
if (!String.IsNullOrEmpty(treatments_mexpirationTextBox.Text))
((treatments)item).treatments_mexpiration = Convert.ToByte(treatments_mexpirationTextBox.Text);
else
((treatments)item).treatments_mexpiration = null;
DGUIGHFData.Update<treatments, DentneDModel>(_dentnedModel.Treatments, item);
}
/// <summary>
/// Remove an item
/// </summary>
/// <param name="item"></param>
private void Remove_tabTreatments(object item)
{
DGUIGHFData.Remove<treatments, DentneDModel>(_dentnedModel.Treatments, item);
}
/// <summary>
/// Unset taxes_id
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_tabTreatments_unsettaxesid_Click(object sender, EventArgs e)
{
taxes_idComboBox.SelectedIndex = -1;
}
#endregion
#region tabTreatmentsPrices
/// <summary>
/// Get tab list DataSource
/// </summary>
/// <returns></returns>
private object GetDataSourceList_tabTreatmentsPrices()
{
object ret = null;
//get current treatment
int treatments_id = -1;
if (vTreatmentsBindingSource.Current != null)
{
treatments_id = (((DataRowView)vTreatmentsBindingSource.Current).Row).Field<int>("treatments_id");
}
//get treatments
List<treatmentsprices> treatmentspricesl = new List<treatmentsprices>();
if (comboBox_tabTreatmentsPrices_filterPriceslists.SelectedIndex != -1 && comboBox_tabTreatmentsPrices_filterPriceslists.SelectedIndex != 0)
{
int treatmentspriceslists_id = Convert.ToInt32(comboBox_tabTreatmentsPrices_filterPriceslists.SelectedValue);
treatmentspricesl = _dentnedModel.TreatmentsPrices.List(r => r.treatments_id == treatments_id && r.treatmentspriceslists_id == treatmentspriceslists_id).ToList();
}
else
treatmentspricesl = _dentnedModel.TreatmentsPrices.List(r => r.treatments_id == treatments_id).ToList();
IEnumerable<VTreatmentsPrices> vTreatmentsPrices =
treatmentspricesl.Select(
r => new VTreatmentsPrices
{
treatmentsprices_id = r.treatmentsprices_id,
price = (double)r.treatmentsprices_price,
pricelist = _dentnedModel.TreatmentsPricesLists.Find(r.treatmentspriceslists_id).treatmentspriceslists_name
}).ToList();
ret = DGDataTableUtils.ToDataTable<VTreatmentsPrices>(vTreatmentsPrices);
return ret;
}
/// <summary>
/// Load the tab DataSource
/// </summary>
/// <returns></returns>
private object GetDataSourceEdit_tabTreatmentsPrices()
{
return DGUIGHFData.LoadEntityFromCurrentBindingSource<treatmentsprices, DentneDModel>(_dentnedModel.TreatmentsPrices, vTreatmentsPricesBindingSource, new string[] { "treatmentsprices_id" });
}
/// <summary>
/// Do actions after Save
/// </summary>
/// <param name="item"></param>
private void AfterSaveAction_tabTreatmentsPrices(object item)
{
DGUIGHFData.SetBindingSourcePosition<treatmentsprices, DentneDModel>(_dentnedModel.TreatmentsPrices, item, vTreatmentsPricesBindingSource);
}
/// <summary>
/// Add an item
/// </summary>
/// <param name="item"></param>
private void Add_tabTreatmentsPrices(object item)
{
DGUIGHFData.Add<treatmentsprices, DentneDModel>(_dentnedModel.TreatmentsPrices, item);
}
/// <summary>
/// Update an item
/// </summary>
/// <param name="item"></param>
private void Update_tabTreatmentsPrices(object item)
{
DGUIGHFData.Update<treatmentsprices, DentneDModel>(_dentnedModel.TreatmentsPrices, item);
}
/// <summary>
/// Remove an item
/// </summary>
/// <param name="item"></param>
private void Remove_tabTreatmentsPrices(object item)
{
DGUIGHFData.Remove<treatmentsprices, DentneDModel>(_dentnedModel.TreatmentsPrices, item);
}
/// <summary>
/// New tab button handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_tabTreatmentsPrices_new_Click(object sender, EventArgs e)
{
if (vTreatmentsBindingSource.Current != null)
{
if (AddClick(tabElement_tabTreatmentsPrices))
{
((treatmentsprices)treatmentspricesBindingSource.Current).treatments_id = (((DataRowView)vTreatmentsBindingSource.Current).Row).Field<int>("treatments_id");
if (comboBox_tabTreatmentsPrices_filterPriceslists.SelectedIndex != -1 && comboBox_tabTreatmentsPrices_filterPriceslists.SelectedIndex != 0)
{
((treatmentsprices)treatmentspricesBindingSource.Current).treatmentspriceslists_id = Convert.ToInt32(comboBox_tabTreatmentsPrices_filterPriceslists.SelectedValue);
treatmentspriceslists_idComboBox.Enabled = false;
}
treatmentspricesBindingSource.ResetBindings(true);
}
}
}
/// <summary>
/// Treatments prices changed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBox_tabTreatmentsPrices_filterPriceslists_SelectedIndexChanged(object sender, EventArgs e)
{
if (IsBindingSourceLoading)
return;
ReloadTab(tabElement_tabTreatmentsPrices);
}
/// <summary>
/// Treatments lists changed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void treatmentspriceslists_idComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (IsBindingSourceLoading)
return;
if (treatmentspriceslists_idComboBox.SelectedIndex != -1 && (tabElement_tabTreatmentsPrices.CurrentEditingMode == EditingMode.C || tabElement_tabTreatmentsPrices.CurrentEditingMode == EditingMode.U))
{
int treatments_id = -1;
if (vTreatmentsBindingSource.Current != null)
{
treatments_id = (((DataRowView)vTreatmentsBindingSource.Current).Row).Field<int>("treatments_id");
}
if (treatments_id != -1)
{
treatments treatments = _dentnedModel.Treatments.Find(treatments_id);
treatmentspriceslists treatmentspriceslist = _dentnedModel.TreatmentsPricesLists.Find(treatmentspriceslists_idComboBox.SelectedValue);
if (treatments != null && treatmentspriceslist != null)
{
((treatmentsprices)treatmentspricesBindingSource.Current).treatmentspriceslists_id = treatmentspriceslist.treatmentspriceslists_id;
((treatmentsprices)treatmentspricesBindingSource.Current).treatmentsprices_price = Math.Round(treatments.treatments_price * treatmentspriceslist.treatmentspriceslists_multiplier, 2);
}
treatmentspricesBindingSource.ResetBindings(true);
}
}
}
#endregion
private void button_tabTreatments_save_Click(object sender, EventArgs e)
{
}
private void treatments_codeTextBox_TextChanged(object sender, EventArgs e)
{
}
}
}
I would suggest reading up on Regular Expressions and download a Regex tool such as Expresso (http://www.ultrapico.com/expresso.htm), or use one of the various online Regex evaluation sites.
"^[A-Z0-9\-]{6}$"
This conflicts with what you are saying about "expecting 3 characters". This Regex will expect 6, and only 6 characters ranging from A-Z, 0-9, and-or a "-". The text message that is added to the errors collection may still say "3 characters" but that is a misleading message that needs to be updated. Whatever changes you do to the regular expression, you should also update the message behind language.text003, wherever that is coming from. (Resource file, database, etc.)
If the updated text box can accept any number of characters, but still needs to be A-Z, 0-9, and "-" then:
"^[A-Z0-9\-]*"
If you want to impose a maximum of 20 characters for example:
"^[A-Z0-9\-]{0,20}$"
If you want a minimum of 6 and maximum of 20:
"^[A-Z0-9\-]{6,20}$"
If you just want to accept any string and remove the character restrictions all-together, then delete that whole if block.
I am using a WPF controller called ZoomableCanvas (see http://blogs.msdn.com/b/kaelr/archive/2010/08/11/zoomableapplication2-a-million-items.aspx), which is a virtualized canvas (renders only what needs to be in the display area). I am trying to implement a method to, given the viewport rectangle, finds objects in the ObservableCollection whose top/left parameters as a point intersect the rectangle. I've got it working, as below:
//This is all in a class that inherits from ObserableCollection<BlockTile>
//Where blocktile is a struct-like class used to store brush and position info for the
//tiles that make up our canvas content.
public IEnumerable<int> Query(Rect rectangle)
{
rectangle.Intersect(Extent); //Extent is our total data set, in case the
//viewport pans past our data set.
foreach (BlockTile t in this)
{
if(rectangle.Contains(new Point(t.left, t.top)))
{
yield return (int)this.IndexOf(t);
}
}
}
The problem with this approach is that my BlockTile collection is around 70,000 items and iterating across the entire data set is killing performance. I'm trying to figure out if, given an IEnumerable of objects, there's a way to filter these items without iterating the entire resultset. My guess is no.
My gut says to try and insert the data in-order and then retrieving it becomes mapping the position in the canvas against the insert-order. However that's clumsy and inexact. The other option is to query the root data source and back into the calculation we used to get the position on the canvas in the first place. Also kind of clumsy, but at least we're removing the full iteration. Just curious if there's any tips or tricks to LINQ to replace the foreach but that doesn't actually do a foreach behind the scenes.
As #Kek alluded to, what you want is a hierarchical collision structure, like a QuadTree. I googled a bit and found what appears to be an ideal implementation (The QuadTree class is shamelessly lifted from GaryTexmo's Generic QuadTree, the example code I threw together)
Sample output from this script:
Filling tree with 10000 rectangles took 00:00:00.0066186
1000 queries of quadtree took 00:00:00.4597303 and avged 4334 hits per query
Code:
void Main()
{
var width = 10000;
var height = 10000;
var rectCount = 10000;
var queryCount = 1000;
var tree = new QuadTree<RectangleHolder>(0, 0, width, height);
var rnd = new Random();
var rects = Enumerable.Range(0, rectCount)
.Select(_ => new { px = new []{rnd.Next(width), rnd.Next(width)}, py = new []{rnd.Next(height), rnd.Next(height)}})
.Select(pts => new Rectangle(pts.px.Min(), pts.py.Min(), pts.px.Max() - pts.px.Min(), pts.py.Max() - pts.py.Min()))
.ToList();
var queryRanges = Enumerable.Range(0, queryCount)
.Select(_ => new { px = new []{rnd.Next(width), rnd.Next(width)}, py = new []{rnd.Next(height), rnd.Next(height)}})
.Select(pts => new Rectangle(pts.px.Min(), pts.py.Min(), pts.px.Max() - pts.px.Min(), pts.py.Max() - pts.py.Min()))
.ToList();
var sw = Stopwatch.StartNew();
foreach (var rect in rects)
{
tree.Insert(new RectangleHolder(rect));
}
Console.WriteLine("Filling tree with {0} rectangles took {1}", rectCount, sw.Elapsed);
sw.Restart();
var totHits = 0;
var res = new List<RectangleHolder>();
foreach (var queryRect in queryRanges)
{
res.Clear();
tree.GetObjects(queryRect, ref res);
totHits += res.Count;
}
Console.WriteLine("{0} queries of quadtree took {1} and avged {2} hits per query", queryCount, sw.Elapsed, (totHits / queryCount));
}
public class RectangleHolder : IHasRectangle
{
public RectangleHolder(Rectangle rect)
{
Rect = rect;
}
public Rectangle Rect {get; private set;}
}
public interface IHasRectangle
{
/// <summary>
/// The rectangle that defines the object's boundaries.
/// </summary>
Rectangle Rect { get; }
}
public class QuadTree<T> where T : IHasRectangle
{
#region Constants
// How many objects can exist in a QuadTree before it sub divides itself
private const int MAX_OBJECTS_PER_NODE = 2;
#endregion
#region Private Members
private List<T> m_objects = null; // The objects in this QuadTree
private Rectangle m_rect; // The area this QuadTree represents
private QuadTree<T> m_childTL = null; // Top Left Child
private QuadTree<T> m_childTR = null; // Top Right Child
private QuadTree<T> m_childBL = null; // Bottom Left Child
private QuadTree<T> m_childBR = null; // Bottom Right Child
#endregion
#region Public Properties
/// <summary>
/// The area this QuadTree represents.
/// </summary>
public Rectangle QuadRect { get { return m_rect; } }
/// <summary>
/// The top left child for this QuadTree
/// </summary>
public QuadTree<T> TopLeftChild { get { return m_childTL; } }
/// <summary>
/// The top right child for this QuadTree
/// </summary>
public QuadTree<T> TopRightChild { get { return m_childTR; } }
/// <summary>
/// The bottom left child for this QuadTree
/// </summary>
public QuadTree<T> BottomLeftChild { get { return m_childBL; } }
/// <summary>
/// The bottom right child for this QuadTree
/// </summary>
public QuadTree<T> BottomRightChild { get { return m_childBR; } }
/// <summary>
/// The objects contained in this QuadTree at it's level (ie, excludes children)
/// </summary>
public List<T> Objects { get { return m_objects; } }
/// <summary>
/// How many total objects are contained within this QuadTree (ie, includes children)
/// </summary>
public int Count { get { return this.ObjectCount(); } }
#endregion
#region Constructor
/// <summary>
/// Creates a QuadTree for the specified area.
/// </summary>
/// <param name="rect">The area this QuadTree object will encompass.</param>
public QuadTree(Rectangle rect)
{
m_rect = rect;
}
/// <summary>
/// Creates a QuadTree for the specified area.
/// </summary>
/// <param name="x">The top-left position of the area rectangle.</param>
/// <param name="y">The top-right position of the area reactangle.</param>
/// <param name="width">The width of the area rectangle.</param>
/// <param name="height">The height of the area rectangle.</param>
public QuadTree(int x, int y, int width, int height)
{
m_rect = new Rectangle(x, y, width, height);
}
#endregion
#region Private Members
/// <summary>
/// Add an item to the object list.
/// </summary>
/// <param name="item">The item to add.</param>
private void Add(T item)
{
if (m_objects == null)
m_objects = new List<T>();
m_objects.Add(item);
}
/// <summary>
/// Remove an item from the object list.
/// </summary>
/// <param name="item">The object to remove.</param>
private void Remove(T item)
{
if (m_objects != null && m_objects.Contains(item))
m_objects.Remove(item);
}
/// <summary>
/// Get the total for all objects in this QuadTree, including children.
/// </summary>
/// <returns>The number of objects contained within this QuadTree and its children.</returns>
private int ObjectCount()
{
int count = 0;
// Add the objects at this level
if (m_objects != null) count += m_objects.Count;
// Add the objects that are contained in the children
if (m_childTL != null)
{
count += m_childTL.ObjectCount();
count += m_childTR.ObjectCount();
count += m_childBL.ObjectCount();
count += m_childBR.ObjectCount();
}
return count;
}
/// <summary>
/// Subdivide this QuadTree and move it's children into the appropriate Quads where applicable.
/// </summary>
private void Subdivide()
{
// We've reached capacity, subdivide...
Point size = new Point(m_rect.Width / 2, m_rect.Height / 2);
Point mid = new Point(m_rect.X + size.X, m_rect.Y + size.Y);
m_childTL = new QuadTree<T>(new Rectangle(m_rect.Left, m_rect.Top, size.X, size.Y));
m_childTR = new QuadTree<T>(new Rectangle(mid.X, m_rect.Top, size.X, size.Y));
m_childBL = new QuadTree<T>(new Rectangle(m_rect.Left, mid.Y, size.X, size.Y));
m_childBR = new QuadTree<T>(new Rectangle(mid.X, mid.Y, size.X, size.Y));
// If they're completely contained by the quad, bump objects down
for (int i = 0; i < m_objects.Count; i++)
{
QuadTree<T> destTree = GetDestinationTree(m_objects[i]);
if (destTree != this)
{
// Insert to the appropriate tree, remove the object, and back up one in the loop
destTree.Insert(m_objects[i]);
Remove(m_objects[i]);
i--;
}
}
}
/// <summary>
/// Get the child Quad that would contain an object.
/// </summary>
/// <param name="item">The object to get a child for.</param>
/// <returns></returns>
private QuadTree<T> GetDestinationTree(T item)
{
// If a child can't contain an object, it will live in this Quad
QuadTree<T> destTree = this;
if (m_childTL.QuadRect.Contains(item.Rect))
{
destTree = m_childTL;
}
else if (m_childTR.QuadRect.Contains(item.Rect))
{
destTree = m_childTR;
}
else if (m_childBL.QuadRect.Contains(item.Rect))
{
destTree = m_childBL;
}
else if (m_childBR.QuadRect.Contains(item.Rect))
{
destTree = m_childBR;
}
return destTree;
}
#endregion
#region Public Methods
/// <summary>
/// Clears the QuadTree of all objects, including any objects living in its children.
/// </summary>
public void Clear()
{
// Clear out the children, if we have any
if (m_childTL != null)
{
m_childTL.Clear();
m_childTR.Clear();
m_childBL.Clear();
m_childBR.Clear();
}
// Clear any objects at this level
if (m_objects != null)
{
m_objects.Clear();
m_objects = null;
}
// Set the children to null
m_childTL = null;
m_childTR = null;
m_childBL = null;
m_childBR = null;
}
/// <summary>
/// Deletes an item from this QuadTree. If the object is removed causes this Quad to have no objects in its children, it's children will be removed as well.
/// </summary>
/// <param name="item">The item to remove.</param>
public void Delete(T item)
{
// If this level contains the object, remove it
bool objectRemoved = false;
if (m_objects != null && m_objects.Contains(item))
{
Remove(item);
objectRemoved = true;
}
// If we didn't find the object in this tree, try to delete from its children
if (m_childTL != null && !objectRemoved)
{
m_childTL.Delete(item);
m_childTR.Delete(item);
m_childBL.Delete(item);
m_childBR.Delete(item);
}
if (m_childTL != null)
{
// If all the children are empty, delete all the children
if (m_childTL.Count == 0 &&
m_childTR.Count == 0 &&
m_childBL.Count == 0 &&
m_childBR.Count == 0)
{
m_childTL = null;
m_childTR = null;
m_childBL = null;
m_childBR = null;
}
}
}
/// <summary>
/// Insert an item into this QuadTree object.
/// </summary>
/// <param name="item">The item to insert.</param>
public void Insert(T item)
{
// If this quad doesn't intersect the items rectangle, do nothing
if (!m_rect.Intersects(item.Rect))
return;
if (m_objects == null ||
(m_childTL == null && m_objects.Count + 1 <= MAX_OBJECTS_PER_NODE))
{
// If there's room to add the object, just add it
Add(item);
}
else
{
// No quads, create them and bump objects down where appropriate
if (m_childTL == null)
{
Subdivide();
}
// Find out which tree this object should go in and add it there
QuadTree<T> destTree = GetDestinationTree(item);
if (destTree == this)
{
Add(item);
}
else
{
destTree.Insert(item);
}
}
}
/// <summary>
/// Get the objects in this tree that intersect with the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to find objects in.</param>
/// <param name="results">A reference to a list that will be populated with the results.</param>
public void GetObjects(Rectangle rect, ref List<T> results)
{
// We can't do anything if the results list doesn't exist
if (results != null)
{
if (rect.Contains(m_rect))
{
// If the search area completely contains this quad, just get every object this quad and all it's children have
GetAllObjects(ref results);
}
else if (rect.Intersects(m_rect))
{
// Otherwise, if the quad isn't fully contained, only add objects that intersect with the search rectangle
if (m_objects != null)
{
for (int i = 0; i < m_objects.Count; i++)
{
if (rect.Intersects(m_objects[i].Rect))
{
results.Add(m_objects[i]);
}
}
}
// Get the objects for the search rectangle from the children
if (m_childTL != null)
{
m_childTL.GetObjects(rect, ref results);
m_childTR.GetObjects(rect, ref results);
m_childBL.GetObjects(rect, ref results);
m_childBR.GetObjects(rect, ref results);
}
}
}
}
/// <summary>
/// Get all objects in this Quad, and it's children.
/// </summary>
/// <param name="results">A reference to a list in which to store the objects.</param>
public void GetAllObjects(ref List<T> results)
{
// If this Quad has objects, add them
if (m_objects != null)
results.AddRange(m_objects);
// If we have children, get their objects too
if (m_childTL != null)
{
m_childTL.GetAllObjects(ref results);
m_childTR.GetAllObjects(ref results);
m_childBL.GetAllObjects(ref results);
m_childBR.GetAllObjects(ref results);
}
}
#endregion
}
public static class Ext
{
public static bool Intersects(this Rectangle lhs, Rectangle rhs)
{
var noIntersect =
(lhs.Right < rhs.Left) ||
(rhs.Right < lhs.Left) ||
(lhs.Bottom < rhs.Top) ||
(rhs.Bottom < lhs.Top);
return !noIntersect;
}
}
I was wondering if anyone could show me a simple way of creating buttons in XNA.
Preferably a way to add a function when its clicked, and a way to add and remove them easily.
I recommend using the NeoForce Controls Library for GUI related problems - it has buttons, among the other useful GUI controls including popup windows, list views, combo boxes, and so on.
If you are writing a button class for the learning experience... well, try learning more about it yourself via Google before asking for help.
ADDENDUM
This is some code that I've written for buttons. Maybe it can serve as a starting point. I use it in my 2D game engine, so it has been debugged and tested.
/// <summary>
/// A control that changes appearance when it is hovered over and triggers some effect when it is clicked
/// </summary>
public class EnigmaButton
{
/// <summary>
/// The method signature for notification when a button is clicked
/// </summary>
/// <param name="sender">EnigmaButton that was clicked</param>
public delegate void OnClickEvent(EnigmaButton sender);
/// <summary>
/// Types of textures used for Enigma Buttons
/// </summary>
public enum TextureType { Normal, Over, Press }
#region Variables
protected IVisualExposer m_ui;
protected Rectangle m_bounds;
IInputExposer m_input;
bool m_over = false, m_press = false, m_wasPressed = false;
Dictionary<TextureType, EnigmaResource<Texture2D>> m_textures;
string m_text, m_name;
EnigmaResource<SpriteFont> m_font;
int m_minTextShadow, m_maxTextShadow;
Color m_textTint;
public event OnClickEvent OnClick;
#endregion
/// <summary>
/// A control that changes appearance when it is hovered over and triggers some effect when it is clicked
/// </summary>
/// <param name="ui">Graphical assets</param>
/// <param name="input">Input exposer for mouse input and XBox controller input</param>
/// <param name="reader">XMLReader for the definition of the controller</param>
/// <param name="pos">Bounds of the controller</param>
public EnigmaButton(IVisualExposer ui, IInputExposer input, XmlReader reader, Rectangle pos)
{
m_ui = ui;
m_bounds = pos;
m_textures = new Dictionary<TextureType, EnigmaResource<Texture2D>>();
m_input = input;
Enabled = true;
#region Reading
string name;
bool started = false, insideText = false;
while (reader.Read())
{
if (reader.MoveToContent() == XmlNodeType.Element)
{
name = reader.Name.ToLower();
if (name == "button")
{
if (started)
throw new Exception("Already started.");
started = true;
m_name = reader.GetAttribute("name") ?? string.Empty;
}
else if (!started)
throw new Exception("Not started");
else if (name == "text")
{
m_font = new EnigmaResource<SpriteFont>();
m_font.Filepath = reader.GetAttribute("font");
string minShadow = reader.GetAttribute("minShadow"), maxShadow = reader.GetAttribute("maxShadow");
m_minTextShadow = minShadow != null ? int.Parse(minShadow) : 0;
m_maxTextShadow = maxShadow != null ? int.Parse(maxShadow) : 2;
m_text = reader.GetAttribute("text") ?? string.Empty;
insideText = true;
m_textTint = Color.White;
}
else if (name == "bounds")
{
insideText = false;
m_bounds = new Rectangle(int.Parse(reader.GetAttribute("x")), int.Parse(reader.GetAttribute("y")),
int.Parse(reader.GetAttribute("width")), int.Parse(reader.GetAttribute("height")));
}
else if (name == "texture")
{
insideText = false;
TextureType texType = (TextureType)Enum.Parse(typeof(TextureType), reader.GetAttribute("type"));
if (m_textures.ContainsKey(texType))
throw new Exception("A texture of type '" + texType.ToString() + "' cannot be registered twice");
EnigmaResource<Texture2D> res = new EnigmaResource<Texture2D>();
res.Filepath = reader.ReadString();
m_textures.Add(texType, res);
}
else if (name == "tint")
{
if (!insideText)
throw new Exception("Tints can only be for text");
float a, r, g, b;
string[] split = reader.ReadString().Split(',');
if (split.Length != 4)
throw new Exception("Colors must be RGBA");
r = float.Parse(split[0].Trim());
g = float.Parse(split[1].Trim());
b = float.Parse(split[2].Trim());
a = float.Parse(split[3].Trim());
m_textTint = new Color(r, g, b, a);
}
}
}
#endregion
if (!m_textures.ContainsKey(TextureType.Normal))
throw new Exception("A button must have at least a '" + TextureType.Normal.ToString() + "' texture");
}
#region Methods
public void Initialize()
{
}
public void LoadContent()
{
EnigmaResource<Texture2D> res;
for (int i = 0; i < m_textures.Count; i++)
{
res = m_textures[m_textures.ElementAt(i).Key];
res.Resource = m_ui.Content.Load<Texture2D>(res.Filepath);
m_textures[m_textures.ElementAt(i).Key] = res;
}
if (m_font.Filepath != null)
m_font.Resource = m_ui.Content.Load<SpriteFont>(m_font.Filepath);
}
public void Update(GameTime gameTime)
{
m_wasPressed = m_press;
m_over = m_bounds.Contains(m_input.MouseX, m_input.MouseY);
m_press = m_over ? m_wasPressed ? m_input.IsMouseLeftPressed || m_input.IsButtonPressed(Buttons.A) : m_input.IsMouseLeftTriggered || m_input.IsButtonTriggered(Buttons.A) : false;
if (!m_wasPressed && m_press && OnClick != null)
OnClick(this);
}
public void Draw(GameTime gameTime)
{
Texture2D toDraw = m_textures[TextureType.Normal].Resource;
if (Enabled)
{
if (m_press && m_textures.ContainsKey(TextureType.Press))
toDraw = m_textures[TextureType.Press].Resource;
else if (m_over && m_textures.ContainsKey(TextureType.Over))
toDraw = m_textures[TextureType.Over].Resource;
}
m_ui.SpriteBatch.Draw(toDraw, m_bounds, Enabled ? Color.White : Color.Gray);
if (m_font.Resource != null)
{
Vector2 pos = new Vector2(m_bounds.X, m_bounds.Y);
Vector2 size = m_font.Resource.MeasureString(m_text);
pos.X += (m_bounds.Width - size.X) / 2;
pos.Y += (m_bounds.Height - size.Y) / 2;
UIHelper.DrawShadowedString(m_ui, m_font.Resource, m_text, pos, m_textTint, m_minTextShadow, m_maxTextShadow);
}
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the name of the button
/// </summary>
public string Name
{
get { return m_name; }
set { m_name = value ?? m_name; }
}
/// <summary>
/// Gets or sets the text drawn in the button.
/// WARNING: Will overflow if the text does not normally fit.
/// </summary>
public string Text
{
get { return m_text; }
set { m_text = value ?? string.Empty; }
}
/// <summary>
/// Gets or sets the bounds of the button
/// </summary>
public Rectangle Bounds
{
get { return m_bounds; }
set { m_bounds = value; }
}
/// <summary>
/// Whether or not the control is enabled
/// </summary>
public bool Enabled { get; set; }
#endregion
}
I have seen threads on many sites regarding extending the gridview control so obviously this will be a duplicate. But I haven't found any that truly extend the control to the extent that you could have custom sorting (with header images), filtering by putting drop downs or textboxes in header columns (on a column by column basis) and custom paging (one that doesn't return all records but just returns the ones requested for the given page).
Are there any good tutorials that show the inner-workings of the gridview and how to override the proper functions? I've seen several snippets here and there but none seem to really work and explain things well.
Any links would be appreciated. Thanks!
I've extended the GridView control myself to allow sorting with images, custom paging (so you can select how many records per page from a drop-down) and a few other things. However, you won't be able to do custom paging that just returns the records for the requested page, as that is something your datasource needs to handle and not the GridView.
All I can really do is give you some code and hope it helps. It's pretty old code (pre C#3.0) but may be of some use:
First of all here's the custom GridView control that extends the standard GridView:
using System;
using System.Collections;
using System.Drawing;
using System.Web.UI.WebControls;
using Diplo.WebControls.DataControls.PagerTemplates;
using Image=System.Web.UI.WebControls.Image;
namespace Diplo.WebControls.DataControls
{
/// <summary>
/// Extended <see cref="GridView"/> with some additional cool properties
/// </summary>
public class DiploGridView : GridView
{
#region Properties
/// <summary>
/// Gets or sets a value indicating whether a sort graphic is shown in column headings
/// </summary>
/// <value><c>true</c> if sort graphic is displayed; otherwise, <c>false</c>.</value>
public bool EnableSortGraphic
{
get
{
object o = ViewState["EnableSortGraphic"];
if (o != null)
{
return (bool)o;
}
return true;
}
set
{
ViewState["EnableSortGraphic"] = value;
}
}
/// <summary>
/// Gets or sets the sort ascending image when <see cref="EnableSortGraphic"/> is <c>true</c>
/// </summary>
public string SortAscendingImage
{
get
{
object o = ViewState["SortAscendingImage"];
if (o != null)
{
return (string)o;
}
return Page.ClientScript.GetWebResourceUrl(GetType(), SharedWebResources.ArrowUpImage);
}
set
{
ViewState["SortAscendingImage"] = value;
}
}
/// <summary>
/// Gets or sets the sort descending image <see cref="EnableSortGraphic"/> is <c>true</c>
/// </summary>
public string SortDescendingImage
{
get
{
object o = ViewState["SortDescendingImage"];
if (o != null)
{
return (string)o;
}
return Page.ClientScript.GetWebResourceUrl(GetType(), SharedWebResources.ArrowDownImage);
}
set
{
ViewState["SortDescendingImage"] = value;
}
}
/// <summary>
/// Gets or sets the custom pager settings mode.
/// </summary>
public CustomPagerMode CustomPagerSettingsMode
{
get
{
object o = ViewState["CustomPagerSettingsMode"];
if (o != null)
{
return (CustomPagerMode)o;
}
return CustomPagerMode.None;
}
set
{
ViewState["CustomPagerSettingsMode"] = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether the columns in the grid can be re-sized in the UI
/// </summary>
/// <value><c>true</c> if column resizing is allowed; otherwise, <c>false</c>.</value>
public bool AllowColumnResizing
{
get
{
object o = ViewState["AllowColumnResizing"];
if (o != null)
{
return (bool)o;
}
return false;
}
set
{
ViewState["AllowColumnResizing"] = value;
}
}
/// <summary>
/// Gets or sets the highlight colour for the row
/// </summary>
public Color RowStyleHighlightColour
{
get
{
object o = ViewState["RowStyleHighlightColour"];
if (o != null)
{
return (Color)o;
}
return Color.Empty;
}
set
{
ViewState["RowStyleHighlightColour"] = value;
}
}
#endregion Properties
#region Enums
/// <summary>
/// Represents additional custom paging modes
/// </summary>
public enum CustomPagerMode
{
/// <summary>
/// No custom paging mode
/// </summary>
None,
/// <summary>
/// Shows the rows drop-down list <i>and</i> the previous and next buttons
/// </summary>
RowsPagePreviousNext,
/// <summary>
/// Only shows the previous and next buttons
/// </summary>
PagePreviousNext
}
#endregion
#region Overridden Events
/// <summary>
/// Initializes the pager row displayed when the paging feature is enabled.
/// </summary>
/// <param name="row">A <see cref="T:System.Web.UI.WebControls.GridViewRow"></see> that represents the pager row to initialize.</param>
/// <param name="columnSpan">The number of columns the pager row should span.</param>
/// <param name="pagedDataSource">A <see cref="T:System.Web.UI.WebControls.PagedDataSource"></see> that represents the data source.</param>
protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
switch (CustomPagerSettingsMode)
{
case CustomPagerMode.RowsPagePreviousNext:
PagerTemplate = new RowsPagePreviousNext(pagedDataSource, this);
break;
case CustomPagerMode.PagePreviousNext:
PagerTemplate = new PagePreviousNext(pagedDataSource, this);
break;
case CustomPagerMode.None:
break;
default:
break;
}
base.InitializePager(row, columnSpan, pagedDataSource);
}
/// <summary>
/// Raises the <see cref="E:System.Web.UI.Control.PreRender"></see> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
protected override void OnPreRender(EventArgs e)
{
if (AllowColumnResizing && Visible)
{
string vars = String.Format("var _DiploGridviewId = '{0}';\n", ClientID);
if (!Page.ClientScript.IsClientScriptBlockRegistered("Diplo_GridViewVars"))
{
Page.ClientScript.RegisterClientScriptBlock(GetType(), "Diplo_GridViewVars", vars, true);
}
Page.ClientScript.RegisterClientScriptInclude("Diplo_GridView.js",
Page.ClientScript.GetWebResourceUrl(GetType(), "Diplo.WebControls.SharedWebResources.Diplo_GridView_Resize.js"));
}
base.OnPreRender(e);
}
/// <summary>
/// Raises the <see cref="E:System.Web.UI.WebControls.GridView.RowCreated"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Web.UI.WebControls.GridViewRowEventArgs"></see> that contains event data.</param>
protected override void OnRowCreated(GridViewRowEventArgs e)
{
if (EnableSortGraphic)
{
if (!((e.Row == null)) && e.Row.RowType == DataControlRowType.Header)
{
foreach (TableCell cell in e.Row.Cells)
{
if (cell.HasControls())
{
LinkButton button = ((LinkButton)(cell.Controls[0]));
if (!((button == null)))
{
Image image = new Image();
image.ImageUrl = "images/default.gif";
image.ImageAlign = ImageAlign.Baseline;
if (SortExpression == button.CommandArgument)
{
image.ImageUrl = SortDirection == SortDirection.Ascending ? SortAscendingImage : SortDescendingImage;
Literal space = new Literal();
space.Text = " ";
cell.Controls.Add(space);
cell.Controls.Add(image);
}
}
}
}
}
}
if (RowStyleHighlightColour != Color.Empty)
{
if (e.Row != null)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Attributes.Add("onmouseover", String.Format("this.style.backgroundColor='{0}'", ColorTranslator.ToHtml(RowStyleHighlightColour)));
e.Row.Attributes.Add("onmouseout", "this.style.backgroundColor=''");
}
}
}
base.OnRowCreated(e);
}
/// <summary>
/// Creates the control hierarchy that is used to render a composite data-bound control based on the values that are stored in view state.
/// </summary>
protected override void CreateChildControls()
{
base.CreateChildControls();
CheckShowPager();
}
private void CheckShowPager()
{
if (CustomPagerSettingsMode != CustomPagerMode.None && AllowPaging)
{
if (TopPagerRow != null)
{
TopPagerRow.Visible = true;
}
if (BottomPagerRow != null)
{
BottomPagerRow.Visible = true;
}
}
}
/// <summary>
/// Creates the control hierarchy used to render the <see cref="T:System.Web.UI.WebControls.GridView"></see> control using the specified data source.
/// </summary>
/// <param name="dataSource">An <see cref="T:System.Collections.IEnumerable"></see> that contains the data source for the <see cref="T:System.Web.UI.WebControls.GridView"></see> control.</param>
/// <param name="dataBinding">true to indicate that the child controls are bound to data; otherwise, false.</param>
/// <returns>The number of rows created.</returns>
protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
int i = base.CreateChildControls(dataSource, dataBinding);
CheckShowPager();
return i;
}
#endregion Overridden Events
}
}
Then there is a custom paging class that is used as a paging template:
using System;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Web.UI;
namespace Diplo.WebControls.DataControls.PagerTemplates
{
/// <summary>
/// Paging template for the <see cref="DiploGridView"/>
/// </summary>
public class RowsPagePreviousNext : ITemplate
{
readonly PagedDataSource _pagedDataSource;
readonly DiploGridView DiploGridView;
/// <summary>
/// Initializes a new instance of the <see cref="RowsPagePreviousNext"/> class.
/// </summary>
/// <param name="pagedDataSource">The <see cref="PagedDataSource"/>.</param>
/// <param name="DiploGrid">A reference to the <see cref="DiploGridView"/>.</param>
public RowsPagePreviousNext(PagedDataSource pagedDataSource, DiploGridView DiploGrid)
{
_pagedDataSource = pagedDataSource;
DiploGridView = DiploGrid;
}
/// <summary>
/// When implemented by a class, defines the <see cref="T:System.Web.UI.Control"></see> object that child controls and templates belong to. These child controls are in turn defined within an inline template.
/// </summary>
/// <param name="container">The <see cref="T:System.Web.UI.Control"></see> object to contain the instances of controls from the inline template.</param>
void ITemplate.InstantiateIn(Control container)
{
Literal space = new Literal();
space.Text = " ";
HtmlGenericControl divLeft = new HtmlGenericControl("div");
divLeft.Style.Add("float", "left");
divLeft.Style.Add(HtmlTextWriterStyle.Width, "25%");
Label lb = new Label();
lb.Text = "Show rows: ";
divLeft.Controls.Add(lb);
DropDownList ddlPageSize = new DropDownList();
ListItem item;
ddlPageSize.AutoPostBack = true;
ddlPageSize.ToolTip = "Select number of rows per page";
int max = (_pagedDataSource.DataSourceCount < 50) ? _pagedDataSource.DataSourceCount : 50;
int i;
const int increment = 5;
bool alreadySelected = false;
for (i = increment; i <= max; i = i + increment)
{
item = new ListItem(i.ToString());
if (i == _pagedDataSource.PageSize)
{
item.Selected = true;
alreadySelected = true;
}
ddlPageSize.Items.Add(item);
}
item = new ListItem("All", _pagedDataSource.DataSourceCount.ToString());
if (_pagedDataSource.DataSourceCount == _pagedDataSource.PageSize && alreadySelected == false)
{
item.Selected = true;
alreadySelected = true;
}
if (_pagedDataSource.DataSourceCount > (i - increment) && alreadySelected == false)
{
item.Selected = true;
}
ddlPageSize.Items.Add(item);
ddlPageSize.SelectedIndexChanged += new EventHandler(ddlPageSize_SelectedIndexChanged);
divLeft.Controls.Add(ddlPageSize);
HtmlGenericControl divRight = new HtmlGenericControl("div");
divRight.Style.Add("float", "right");
divRight.Style.Add(HtmlTextWriterStyle.Width, "75%");
divRight.Style.Add(HtmlTextWriterStyle.TextAlign, "right");
Literal lit = new Literal();
lit.Text = String.Format("Found {0} record{1}. Page ",
_pagedDataSource.DataSourceCount,
(_pagedDataSource.DataSourceCount == 1) ? String.Empty : "s" );
divRight.Controls.Add(lit);
TextBox tbPage = new TextBox();
tbPage.ToolTip = "Enter page number";
tbPage.Columns = 2;
tbPage.MaxLength = 3;
tbPage.Text = (_pagedDataSource.CurrentPageIndex + 1).ToString();
tbPage.CssClass = "pagerTextBox";
tbPage.AutoPostBack = true;
tbPage.TextChanged += new EventHandler(tbPage_TextChanged);
divRight.Controls.Add(tbPage);
if (_pagedDataSource.PageCount < 2)
tbPage.Enabled = false;
lit = new Literal();
lit.Text = " of " + _pagedDataSource.PageCount;
divRight.Controls.Add(lit);
divRight.Controls.Add(space);
Button btn = new Button();
btn.Text = "";
btn.CommandName = "Page";
btn.CommandArgument = "Prev";
btn.SkinID = "none";
btn.Enabled = !_pagedDataSource.IsFirstPage;
btn.CssClass = (btn.Enabled) ? "buttonPreviousPage" : "buttonPreviousPageDisabled";
if (btn.Enabled)
btn.ToolTip = "Previous page";
divRight.Controls.Add(btn);
btn = new Button();
btn.Text = "";
btn.CommandName = "Page";
btn.CommandArgument = "Next";
btn.SkinID = "none";
btn.CssClass = "buttonNext";
btn.Enabled = !_pagedDataSource.IsLastPage;
btn.CssClass = (btn.Enabled) ? "buttonNextPage" : "buttonNextPageDisabled";
if (btn.Enabled)
btn.ToolTip = "Next page";
divRight.Controls.Add(btn);
container.Controls.Add(divLeft);
container.Controls.Add(divRight);
}
/// <summary>
/// Handles the TextChanged event of the tbPage control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
void tbPage_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
int page;
if (int.TryParse(tb.Text, out page))
{
if (page <= _pagedDataSource.PageCount && page > 0)
{
DiploGridView.PageIndex = page - 1;
}
}
}
}
/// <summary>
/// Handles the SelectedIndexChanged event of the ddlPageSize control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList list = sender as DropDownList;
if (list != null) DiploGridView.PageSize = Convert.ToInt32(list.SelectedValue);
}
}
}
I can't really talk you through it as server controls are complex, I just hope it gives you some help.