by Joche Ojeda | Jan 2, 2025 | XtraReports
Introduction šÆ
If you’re familiar with Windows Forms development, transitioning to XtraReports will feel remarkably natural. This guide explores how XtraReports leverages familiar Windows Forms concepts while extending them for robust reporting capabilities.
š” Quick Tip: Think of XtraReports as Windows Forms optimized for paper output instead of screen output!
A Personal Journey āØ
Microsoft released .NET Framework in late 2002. At the time, I was a VB6 developer, relying on Crystal Reports 7 for reporting. By 2003, my team was debating whether to transition to this new thing called .NET. We were concerned about VB6ās longevityāthinking it had just a couple more years left. How wrong we were! Even today, VB6 applications are still running in some places (it’s January 2, 2025, as I write this).
Back in the VB6 era, we used the Crystal Reports COM object to integrate reports. When we finally moved to .NET Framework, we performed some “black magic” to continue using our existing 700 reports across nine countries. The decision to fully embrace .NET was repeatedly delayed due to the sheer volume of reports we had to manage. Our ultimate goal was to unify our reporting and parameter forms within a single development environment.
This led us to explore other technologies. While considering Delphi, we discovered DevExpress. My boss procured our first DevExpress .NET license for Windows Forms, marking the start of my adventure with DevExpress and XtraReports. Initially, transitioning from the standalone Crystal Report Designer to the IDE-based XtraReports Designer was challenging. To better understand how XtraReports worked, I decided to write reports programmatically instead of using the visual designer.
Architectural Similarities š½ļø
XtraReports mirrors many fundamental Windows Forms concepts:
Source |
Destination |
XtraReport Class |
Report Designer Surface |
XtraReport Class |
Control Container |
XtraReport Class |
Event System |
XtraReport Class |
Properties Window |
Control Container |
Labels & Text |
Control Container |
Tables & Grids |
Control Container |
Images & Charts |
Report Designer Surface |
Control Toolbox |
Report Designer Surface |
Design Surface |
Report Designer Surface |
Preview Window |
Like how Windows Forms applications start with a Form
class, XtraReports begin with an XtraReport
base class. Both serve as containers that can:
- Host other controls
- Manage layout
- Handle events
- Support data binding
Visual Designer Experience šØ
The design experience remains consistent with Windows Forms:
Windows Forms |
XtraReports |
Form Designer |
Report Designer |
Toolbox |
Report Controls |
Properties Window |
Properties Grid |
Component Tray |
Component Tool |
Control Ecosystem š§°
XtraReports provides analogous controls to Windows Forms:
// Windows Forms
public partial class CustomerForm : Form
{
private Label customerNameLabel;
private DataGridView orderDetailsGrid;
}
// XtraReports
public partial class CustomerReport : XtraReport
{
private XRLabel customerNameLabel;
private XRTable orderDetailsTable;
}
Common control mappings:
- Label ā”ļø XRLabel
- Panel ā”ļø XRPanel
- PictureBox ā”ļø XRPictureBox
- DataGridView ā”ļø XRTable
- GroupBox ā”ļø Band
- UserControl ā”ļø Subreport
Data Binding Patterns š
The data binding syntax maintains familiarity:
// Windows Forms data binding
customerNameLabel.DataBindings.Add("Text", customerDataSet, "Customers.Name");
// XtraReports data binding
customerNameLabel.ExpressionBindings.Add(
new ExpressionBinding("Text", "[Name]"));
Code Architecture šļø
The code-behind model remains consistent:
public partial class CustomerReport : DevExpress.XtraReports.UI.XtraReport
{
public CustomerReport()
{
InitializeComponent(); // Familiar Windows Forms pattern
}
private void CustomerReport_BeforePrint(object sender, PrintEventArgs e)
{
// Event handling similar to Windows Forms
// Instead of Form_Load, we have Report_BeforePrint
}
}
Key Differences ā”
While similarities abound, important differences exist:
- Output Focus šØļø
- Windows Forms: Screen-based interaction
- XtraReports: Print/export optimization
- Layout Model š
- Windows Forms: Flexible screen layouts
- XtraReports: Page-based layouts with bands
- Control Behavior š®
- Windows Forms: Interactive controls
- XtraReports: Display-oriented controls
- Data Processing šļø
- Windows Forms: Real-time data interaction
- XtraReports: Batch data processing
Some Advices š
- Design Philosophy
// Think in terms of paper output
public class InvoiceReport : XtraReport
{
protected override void OnBeforePrint(PrintEventArgs e)
{
// Calculate page breaks
// Optimize for printing
}
}
- Layout Strategy
- Use bands for logical grouping
- Consider paper size constraints
- Plan for different export formats
- Data Handling
- Pre-process data when possible
- Use calculated fields for complex logic
- Consider subreports for complex layouts
by Joche Ojeda | Oct 15, 2024 | A.I, Semantic Kernel, XAF, XPO
A few weeks ago, I forked the Semantic Kernel repository to experiment with it. One of my first experiments was to create a memory provider for XPO. The task was not too difficult; basically, I needed to implement the IMemoryStore interface, add some XPO boilerplate code, and just like that, we extended the Semantic Kernel memory store to support 10+ databases. You can check out the code for the XpoMemoryStore here.
My initial goal in creating the XpoMemoryStore was simply to see if XPO would be a good fit for handling embeddings. Spoiler alert: it was! To understand the basic functionality of the plugin, you can take a look at the integration test here.
As you can see, usage is straightforward. You start by connecting to the database that handles embedding collections, and all you need is a valid XPO connection string:
using XpoMemoryStore db = await XpoMemoryStore.ConnectAsync("XPO connection string");
In my original design, everything worked fine, but I faced some challenges when trying to use my new XpoMemoryStore in XAF. Hereās what I encountered:
- The implementation of XpoMemoryStore uses its own data layer, which can lead to issues. This needs to be rewritten to use the same data layer as XAF.
- The XpoEntry implementation cannot be extended. In some use cases, you might want to use a different object to store the embeddings, perhaps one that has an association with another object.
To address these problems, I introduced the IXpoEntryManager interface. The goal of this interface is to handle object creation and queries.
public interface IXpoEntryManager
{
T CreateObject();
public event EventHandler ObjectCreatedEvent;
void Commit();
IQueryable GetQuery(bool inTransaction = true);
void Delete(object instance);
void Dispose();
}
Now, object creation is handled through the CreateObject<T>
method, allowing the underlying implementation to be changed to use a UnitOfWork
or ObjectSpace
. Thereās also the ObjectCreatedEvent
event, which lets you access the newly created object in case you need to associate it with another object. Lastly, the GetQuery<T>
method enables redirecting the search for records to a different type.
Iāll keep updating the code as needed. If youād like to discuss AI, XAF, or .NET, feel free to schedule a meeting: Schedule a Meeting with us.
Until next time, XAF out!
Related Article
https://www.jocheojeda.com/2024/09/04/using-the-imemorystore-interface-and-devexpress-xpo-orm-to-implement-a-custom-memory-store-for-semantic-kernel/
by Joche Ojeda | Jul 3, 2024 | Uncategorized
Hey there, fellow developers! Today, letās talk about a practice that can revolutionize the way we create, test, and perfect our software: dogfooding. If youāre wondering what dogfooding means, donāt worry, itās not about what you feed your pets. In the tech world, āeating your own dog foodā means using the software you develop in your day-to-day operations. Letās dive into how this can be a game-changer for us.
Why Should We Dogfood?
- Catch Bugs Early: By using our own software, we become our first line of defense against bugs and glitches. Real-world usage uncovers issues that might slip through traditional testing. We get to identify and fix these problems before they ever reach our users.
- Enhance Quality Assurance: Thereās no better way to ensure our software meets high standards than by relying on it ourselves. When our own work depends on our product, we naturally aim for higher quality and reliability.
- Improve User Experience: When we step into the shoes of our users, we experience firsthand what works well and what doesnāt. This unique perspective allows us to design more intuitive and user-friendly software.
- Create a Rapid Feedback Loop: Using our software internally means continuous and immediate feedback. This quick loop helps us iterate faster, refining features and squashing bugs swiftly.
- Build Credibility and Trust: When we show confidence in our software by using it ourselves, it sends a strong message to our users. It demonstrates that we believe in what weāve created, enhancing our credibility and trustworthiness.
Real-World Examples
- Microsoft: Theyāre known for using early versions of Windows and Office within their own teams. This practice helps them catch issues early and improve their products before public release.
- Google: Googlers use beta versions of products like Gmail and Chrome. This internal testing helps them refine their offerings based on real-world use.
- Slack: Slackās team relies on Slack for communication, constantly testing and improving the platform from the inside.
How to Start Dogfooding
- Integrate it Into Daily Work: Start by using your software for internal tasks. Whether itās a project management tool, a communication app, or a new feature, make it part of your teamās daily routine.
- Encourage Team Participation: Get everyone on board. The more diverse the users, the more varied the feedback. Encourage your team to report bugs, suggest improvements, and share their experiences.
- Set Up Feedback Channels: Create dedicated channels for feedback. This could be as simple as a Slack channel or a more structured feedback form. Ensure that the feedback loop is easy and accessible.
- Iterate Quickly: Use the feedback to make quick improvements. Prioritize issues that affect usability and functionality. Show your team that their feedback is valued and acted upon.
Overcoming Challenges
- Avoid Bias: While familiarity is great, it can also lead to bias. Pair internal testing with external beta testers to get a well-rounded perspective.
- Manage Resources: Smaller teams might find it challenging to allocate resources for internal use. Start small and gradually integrate more aspects of your software into daily use.
- Consider Diverse Use Cases: Remember, your internal environment might not replicate all the conditions your users face. Keep an eye on diverse scenarios and edge cases.
Conclusion
Dogfooding is more than just a quirky industry term. Itās a powerful practice that can elevate the quality of our software, speed up our development cycles, and build stronger trust with our users. By using our software as our customers do, we gain invaluable insights that can lead to better, more reliable products. So, letās embrace the dogfood, turn our critical eye inward, and create software that weāre not just proud of but genuinely rely on. Happy coding, and happy dogfooding! š¶š»
Feel free to share your dogfooding experiences in the comments below. Letās learn from each other and continue to improve our craft together!
by Joche Ojeda | Jun 25, 2024 | Object-Oriented Programming
Aristotle and the “Organon”: Foundations of Logical Thought
Aristotle, one of the greatest philosophers of ancient Greece, made substantial contributions to a wide range of fields, including logic, metaphysics, ethics, politics, and natural sciences. Born in 384 BC, Aristotle was a student of Plato and later became the tutor of Alexander the Great. His works have profoundly influenced Western thought for centuries.
One of Aristotle’s most significant contributions is his collection of works on logic known as the “Organon.” This term, which means “instrument” or “tool” in Greek, reflects Aristotle’s view that logic is the tool necessary for scientific and philosophical inquiry. The “Organon” comprises six texts:
- Categories: Classification of terms and predicates.
- On Interpretation: Relationship between language and logic.
- Prior Analytics: Theory of syllogism and deductive reasoning.
- Posterior Analytics: Nature of scientific knowledge.
- Topics: Methods for constructing and deconstructing arguments.
- On Sophistical Refutations: Identification of logical fallacies.
Together, these works lay the groundwork for formal logic, providing a systematic approach to reasoning that is still relevant today.
Object-Oriented Programming (OOP): Building Modern Software
Now, let’s fast-forward to the modern world of software development. Object-Oriented Programming (OOP) is a programming paradigm that has revolutionized the way we write and organize code. At its core, OOP is about creating “objects” that combine data and behavior. Here’s a quick rundown of its fundamental concepts:
- Classes and Objects: A class is a blueprint for creating objects. An object is an instance of a class, containing data (attributes) and methods (functions that operate on the data).
- Inheritance: This allows a class to inherit properties and methods from another class, promoting code reuse.
- Encapsulation: This principle hides the internal state of objects and only exposes a controlled interface, ensuring modularity and reducing complexity.
- Polymorphism: This allows objects to be treated as instances of their parent class rather than their actual class, enabling flexible and dynamic behavior.
- Abstraction: This simplifies complex systems by modeling classes appropriate to the problem.
Bridging Ancient Logic with Modern Programming
You might be wondering, how do Aristotleās ancient logical works relate to Object-Oriented Programming? Surprisingly, they share some fundamental principles!
- Categorization and Classes:
- Aristotle: Categorized different types of predicates and subjects to understand their nature.
- OOP: Classes categorize data and behavior, helping organize and structure code.
- Propositions and Methods:
- Aristotle: Propositions form the basis of logical arguments.
- OOP: Methods define the behaviors and actions of objects, forming the basis of interactions in software.
- Systematic Organization:
- Aristotle: His systematic approach to logic ensures consistency and coherence.
- OOP: Organizes code in a modular and systematic way, promoting maintainability and scalability.
- Error Handling:
- Aristotle: Identified and corrected logical fallacies to ensure sound reasoning.
- OOP: Debugging involves identifying and fixing errors in code, ensuring reliability.
- Modularity and Encapsulation:
- Aristotle: His logical categories and propositions encapsulate different aspects of knowledge, ensuring clarity.
- OOP: Encapsulation hides internal states and exposes a controlled interface, managing complexity.
Conclusion: Timeless Principles
Both Aristotleās “Organon” and Object-Oriented Programming aim to create structured, logical, and efficient systems. While Aristotleās work laid the foundation for logical reasoning, OOP has revolutionized software development with its systematic approach to code organization. By understanding the parallels between these two, we can appreciate the timeless nature of logical and structured thinking, whether applied to ancient philosophy or modern technology.
In a world where technology constantly evolves, grounding ourselves in the timeless principles of logical organization can help us navigate and create with clarity and precision. Whether youāre structuring an argument or designing a software system, these principles are your trusty tools for success.
by Joche Ojeda | Jun 10, 2024 | C#, Data Synchronization, dotnet
The Ephemeral State of SOLID Code: Capturing the Perfect Snapshot
In the world of software development, the SOLID principles are often upheld as the gold standard for designing maintainable and scalable code. These principles ā Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion ā form the bedrock of robust object-oriented design. However, achieving a state where code fully adheres to these principles is a fleeting moment, much like capturing a perfect snapshot in time.
What Does It Mean for Code to Be in a SOLID State?
A SOLID state in source code is a condition where the code perfectly aligns with all five SOLID principles. This means:
- Single Responsibility Principle (SRP): Every class has one, and only one, reason to change.
- Open/Closed Principle (OCP): Software entities should be open for extension but closed for modification.
- Liskov Substitution Principle (LSP): Subtypes must be substitutable for their base types.
- Interface Segregation Principle (ISP): No client should be forced to depend on methods it does not use.
- Dependency Inversion Principle (DIP): Depend on abstractions, not concretions.
In this state, the codebase is a model of clarity, flexibility, and robustness. But this state is inherently transient.
The Moment of SOLID Perfection
The reality of software development is that code is in a constant state of flux. New features are added, bugs are fixed, and refactoring is a continuous process. During these periods of active development, maintaining perfect adherence to SOLID principles is challenging. The code may temporarily violate one or more principles as developers refactor or introduce new functionality.
The truly SOLID state can thus be seen as a snapshot ā a moment frozen in time when the code perfectly adheres to all five principles. This moment typically occurs:
- Post-Refactoring: After a significant refactoring effort, where the focus has been on aligning the code with SOLID principles.
- Before Major Changes: Just before starting a new major feature or overhaul, the existing codebase might be in a perfect SOLID state.
- Code Reviews: Following a rigorous code review process, where adherence to SOLID principles is explicitly checked and enforced.
- Milestone Deliveries: Before delivering a major milestone or release, when the code is thoroughly tested and cleaned up.
The Nature of Active Development
Active development is a chaotic process. As new requirements emerge and priorities shift, developers might temporarily sacrifice adherence to SOLID principles for the sake of rapid progress or to meet deadlines. This is a natural part of the development cycle. The key is to recognize that while the code may deviate from these principles during active development, the goal is to continually steer it back towards a SOLID state.
The SOLID State as Nirvana
Achieving a perfect SOLID state can be likened to reaching nirvana ā an ideal that is almost impossible to fully attain. Just as nirvana represents a state of ultimate peace and enlightenment, a perfectly SOLID codebase represents the pinnacle of software design. However, this state is incredibly difficult to reach and even harder to maintain. Therefore, it is more practical to view adherence to SOLID principles as a spectrum rather than a binary state.
Measuring SOLID Adherence
Instead of aiming for an elusive perfect state, itās more pragmatic to measure adherence to SOLID principles using metrics. Tools and techniques can help quantify how well your code aligns with each principle, providing a percentage that reflects its current state. These metrics can include:
- Class Responsibility: Assessing the number of responsibilities each class has to evaluate adherence to SRP.
- Change Impact Analysis: Measuring the extent to which modifications to the code require changes in other parts of the system, reflecting adherence to OCP.
- Subtype Tests: Ensuring subclasses can replace their base classes without altering the correctness of the program, in line with LSP.
- Interface Utilization: Analyzing the usage of interfaces to ensure they are not overly broad, adhering to ISP.
- Dependency Metrics: Evaluating dependencies between high-level and low-level modules, supporting DIP.
By regularly measuring these metrics, developers can maintain a clear view of how their code is evolving in relation to SOLID principles. This approach allows for continuous improvement and helps teams prioritize refactoring efforts where they are most needed.
Embracing the Snapshot
Understanding that a perfectly SOLID state is a temporary snapshot can help developers maintain a healthy perspective. Itās crucial to strive for SOLID principles as a guiding star but also to accept that deviations are part of the journey. Regular refactoring sessions, continuous integration practices, and diligent code reviews are essential practices to frequently bring the code back to a SOLID state.
Conclusion
In conclusion, a SOLID state of source code is a valuable but ephemeral achievement, akin to reaching nirvana in the realm of software development. It represents a moment of perfection in the ongoing evolution of a software project. By recognizing this, developers can better manage their expectations and maintain a balance between striving for perfection and the practical realities of software development. Embrace the snapshot of SOLID perfection when it occurs, but also understand that the true measure of a healthy codebase is its ability to evolve while frequently realigning with these timeless principles, using metrics and percentages to guide the way.